diff options
61 files changed, 1331 insertions, 1989 deletions
diff --git a/Android.mk b/Android.mk index a1d91c363b0b..8f06d2daf5c1 100644 --- a/Android.mk +++ b/Android.mk @@ -146,8 +146,6 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/ISerialManager.aidl \ core/java/android/hardware/display/IDisplayManager.aidl \ core/java/android/hardware/display/IDisplayManagerCallback.aidl \ - core/java/android/hardware/hdmi/IHdmiCecListener.aidl \ - core/java/android/hardware/hdmi/IHdmiCecService.aidl \ core/java/android/hardware/hdmi/IHdmiControlCallback.aidl \ core/java/android/hardware/hdmi/IHdmiControlService.aidl \ core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl \ @@ -306,14 +304,15 @@ LOCAL_SRC_FILES += \ media/java/android/media/IRemoteVolumeObserver.aidl \ media/java/android/media/IRingtonePlayer.aidl \ media/java/android/media/IVolumeController.aidl \ - media/java/android/media/routeprovider/IRouteConnection.aidl \ - media/java/android/media/routeprovider/IRouteProvider.aidl \ - media/java/android/media/routeprovider/IRouteProviderCallback.aidl \ - media/java/android/media/session/ISessionController.aidl \ - media/java/android/media/session/ISessionControllerCallback.aidl \ - media/java/android/media/session/ISession.aidl \ - media/java/android/media/session/ISessionCallback.aidl \ - media/java/android/media/session/ISessionManager.aidl \ + media/java/android/media/routeprovider/IRouteConnection.aidl \ + media/java/android/media/routeprovider/IRouteProvider.aidl \ + media/java/android/media/routeprovider/IRouteProviderCallback.aidl \ + media/java/android/media/session/IActiveSessionsListener.aidl \ + media/java/android/media/session/ISessionController.aidl \ + media/java/android/media/session/ISessionControllerCallback.aidl \ + media/java/android/media/session/ISession.aidl \ + media/java/android/media/session/ISessionCallback.aidl \ + media/java/android/media/session/ISessionManager.aidl \ media/java/android/media/tv/ITvInputClient.aidl \ media/java/android/media/tv/ITvInputHardware.aidl \ media/java/android/media/tv/ITvInputHardwareCallback.aidl \ diff --git a/api/current.txt b/api/current.txt index bb7e57d9fb0e..e4f2b9730d6f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -705,7 +705,6 @@ package android { field public static final int l_resource_pad23 = 16843770; // 0x10103fa field public static final int l_resource_pad24 = 16843769; // 0x10103f9 field public static final int l_resource_pad25 = 16843768; // 0x10103f8 - field public static final int l_resource_pad26 = 16843767; // 0x10103f7 field public static final int l_resource_pad3 = 16843790; // 0x101040e field public static final int l_resource_pad4 = 16843789; // 0x101040d field public static final int l_resource_pad5 = 16843788; // 0x101040c @@ -1247,6 +1246,7 @@ package android { field public static final int trimPathEnd = 16843813; // 0x1010425 field public static final int trimPathOffset = 16843814; // 0x1010426 field public static final int trimPathStart = 16843812; // 0x1010424 + field public static final int tvInputType = 16843767; // 0x10103f7 field public static final int type = 16843169; // 0x10101a1 field public static final int typeface = 16842902; // 0x1010096 field public static final int uiOptions = 16843672; // 0x1010398 @@ -4483,6 +4483,7 @@ package android.app { field public static final int FLAG_ONGOING_EVENT = 2; // 0x2 field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8 field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1 + field public static final java.lang.String INTENT_CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES"; field public static final int PRIORITY_DEFAULT = 0; // 0x0 field public static final int PRIORITY_HIGH = 1; // 0x1 field public static final int PRIORITY_LOW = -1; // 0xffffffff @@ -6993,7 +6994,6 @@ package android.content { field public static final java.lang.String DISPLAY_SERVICE = "display"; field public static final java.lang.String DOWNLOAD_SERVICE = "download"; field public static final java.lang.String DROPBOX_SERVICE = "dropbox"; - field public static final java.lang.String HDMI_CEC_SERVICE = "hdmi_cec"; field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; @@ -7462,7 +7462,6 @@ package android.content { field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER"; field public static final java.lang.String CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK"; field public static final java.lang.String CATEGORY_MONKEY = "android.intent.category.MONKEY"; - field public static final java.lang.String CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES"; field public static final java.lang.String CATEGORY_OPENABLE = "android.intent.category.OPENABLE"; field public static final java.lang.String CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE"; field public static final java.lang.String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE"; @@ -8369,7 +8368,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location"; field public static final java.lang.String FEATURE_LOCATION_GPS = "android.hardware.location.gps"; field public static final java.lang.String FEATURE_LOCATION_NETWORK = "android.hardware.location.network"; - field public static final java.lang.String FEATURE_MANAGEDPROFILES = "android.software.managedprofiles"; + field public static final java.lang.String FEATURE_MANAGED_PROFILES = "android.software.managed_profiles"; field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone"; field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc"; field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce"; @@ -12828,21 +12827,6 @@ package android.hardware.hdmi { field public static final int UNKNOWN_VENDOR_ID = 16777215; // 0xffffff } - public final class HdmiCecClient { - method public boolean isTvOn(); - method public void sendActiveSource(); - method public void sendGiveDevicePowerStatus(int); - method public void sendImageViewOn(); - method public void sendInactiveSource(); - method public void sendTextViewOn(); - } - - public static abstract class HdmiCecClient.Listener { - ctor public HdmiCecClient.Listener(); - method public void onCableStatusChanged(boolean); - method public void onMessageReceived(android.hardware.hdmi.HdmiCecMessage); - } - public final class HdmiCecDeviceInfo implements android.os.Parcelable { method public int describeContents(); method public int getDeviceType(); @@ -12854,10 +12838,6 @@ package android.hardware.hdmi { field public static final android.os.Parcelable.Creator CREATOR; } - public final class HdmiCecManager { - method public android.hardware.hdmi.HdmiCecClient getClient(int, android.hardware.hdmi.HdmiCecClient.Listener); - } - public final class HdmiCecMessage implements android.os.Parcelable { ctor public HdmiCecMessage(int, int, int, byte[]); method public int describeContents(); @@ -15861,7 +15841,7 @@ package android.media.tv { field public static final int SERVICE_TYPE_OTHER = 0; // 0x0 field public static final int TYPE_1SEG = 263168; // 0x40400 field public static final int TYPE_ATSC_C = 197120; // 0x30200 - field public static final int TYPE_ATSC_M_H = 197120; // 0x30200 + field public static final int TYPE_ATSC_M_H = 197376; // 0x30300 field public static final int TYPE_ATSC_T = 196608; // 0x30000 field public static final int TYPE_CMMB = 327936; // 0x50100 field public static final int TYPE_DTMB = 327680; // 0x50000 @@ -15928,9 +15908,14 @@ package android.media.tv { method public android.content.Intent getIntentForSettingsActivity(); method public android.content.Intent getIntentForSetupActivity(); method public android.content.pm.ServiceInfo getServiceInfo(); + method public int getType(); method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager); method public void writeToParcel(android.os.Parcel, int); field public static final java.lang.String EXTRA_SERVICE_NAME = "serviceName"; + field public static final int TYPE_HDMI = 1; // 0x1 + field public static final int TYPE_PASSTHROUGH = 3; // 0x3 + field public static final int TYPE_TUNER = 2; // 0x2 + field public static final int TYPE_VIRTUAL = 0; // 0x0 } public final class TvInputManager { @@ -15968,11 +15953,12 @@ package android.media.tv { method public void setOverlayViewEnabled(boolean); } - public class TvView extends android.view.SurfaceView { + public class TvView extends android.view.ViewGroup { ctor public TvView(android.content.Context); ctor public TvView(android.content.Context, android.util.AttributeSet); ctor public TvView(android.content.Context, android.util.AttributeSet, int); method public boolean dispatchUnhandledInputEvent(android.view.InputEvent); + method protected void onLayout(boolean, int, int, int, int); method public boolean onUnhandledInputEvent(android.view.InputEvent); method public void reset(); method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index ac25a5315e3a..5bbc43cba2cf 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -57,9 +57,7 @@ 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.HdmiControlManager; -import android.hardware.hdmi.IHdmiCecService; import android.hardware.hdmi.IHdmiControlService; import android.hardware.camera2.CameraManager; import android.hardware.display.DisplayManager; @@ -384,12 +382,6 @@ 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(HDMI_CONTROL_SERVICE, new StaticServiceFetcher() { public Object createStaticService() { IBinder b = ServiceManager.getService(HDMI_CONTROL_SERVICE); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 72b5cd90d5c5..a1cdf59ec2fb 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -17,6 +17,8 @@ package android.app; import android.annotation.IntDef; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -74,6 +76,15 @@ public class Notification implements Parcelable private static final String TAG = "Notification"; /** + * An activity that provides a user interface for adjusting notification preferences for its + * containing application. Optional but recommended for apps that post + * {@link android.app.Notification Notifications}. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES + = "android.intent.category.NOTIFICATION_PREFERENCES"; + + /** * Use all default values (where applicable). */ public static final int DEFAULT_ALL = ~0; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 9baac3244ee1..571bb4d2ea1a 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2148,8 +2148,6 @@ 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 @@ -2638,17 +2636,6 @@ 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 - */ - // TODO: Remove this once HdmiControlService is ready. - public static final String HDMI_CEC_SERVICE = "hdmi_cec"; - - /** - * Use with {@link #getSystemService} to retrieve a * {@link android.hardware.hdmi.HdmiControlManager} for controlling and managing * HDMI-CEC protocol. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index bd07470ee583..fad3851f55b8 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2976,14 +2976,6 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE"; - /** - * An activity that provides a user interface for adjusting notification preferences for its - * containing application. Optional but recommended for apps that post - * {@link android.app.Notification Notifications}. - */ - @SdkConstant(SdkConstantType.INTENT_CATEGORY) - public static final String CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES"; - // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Application launch intent categories (see addCategory()). diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 35bcc027bd0f..aadb10ebad25 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1402,7 +1402,7 @@ public abstract class PackageManager { * The device supports managed profiles for enterprise users. */ @SdkConstant(SdkConstantType.FEATURE) - public static final String FEATURE_MANAGEDPROFILES = "android.software.managedprofiles"; + public static final String FEATURE_MANAGED_PROFILES = "android.software.managed_profiles"; /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: diff --git a/core/java/android/hardware/hdmi/HdmiCecClient.java b/core/java/android/hardware/hdmi/HdmiCecClient.java deleted file mode 100644 index dcb362457e83..000000000000 --- a/core/java/android/hardware/hdmi/HdmiCecClient.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.hardware.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() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * Send <Inactive Source> message. - */ - public void sendInactiveSource() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * Send <Text View On> message. - */ - public void sendTextViewOn() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * Send <Image View On> message. - */ - public void sendImageViewOn() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * 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) { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - } - - /** - * 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() { - Log.w(TAG, "In transition to HdmiControlManager. Will not work."); - return true; - } -} diff --git a/core/java/android/hardware/hdmi/HdmiCecManager.java b/core/java/android/hardware/hdmi/HdmiCecManager.java deleted file mode 100644 index 03c46d881465..000000000000 --- a/core/java/android/hardware/hdmi/HdmiCecManager.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.hardware.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) { - return HdmiCecClient.create(mService, 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/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl deleted file mode 100644 index ecdd345eec4c..000000000000 --- a/core/java/android/hardware/hdmi/IHdmiCecService.aidl +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.hardware.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/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index f642d01e8020..4fa04a910d69 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -6722,5 +6722,19 @@ <!-- Component name of an activity that allows the user to modify the settings for this service. --> <attr name="settingsActivity" /> + <!-- Type of this service. --> + <attr name="tvInputType"> + <!-- Should be in sync with constant values defined in + {@link android.media.tv.TvInputInfo}. --> + + <!-- Virtual input (default) --> + <enum name="virtual" value="0" /> + <!-- HDMI --> + <enum name="hdmi" value="1" /> + <!-- Built-in tuner --> + <enum name="tuner" value="2" /> + <!-- Pass-through --> + <enum name="passthrough" value="3" /> + </attr> </declare-styleable> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 33bdf58167eb..b3e111b4abee 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2097,6 +2097,7 @@ <public type="attr" name="isGame" id="0x10103f4" /> <public type="attr" name="allowEmbedded" id="0x10103f5" /> <public type="attr" name="setupActivity" id="0x10103f6"/> + <public type="attr" name="tvInputType" id="0x10103f7"/> <!-- =============================================================== Resources added in version 21 of the platform diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index a4d491d86b68..1da02156d804 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -28,6 +28,8 @@ import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.display.WifiDisplay; import android.hardware.display.WifiDisplayStatus; +import android.media.session.MediaSession; +import android.media.session.RemoteVolumeProvider; import android.os.Handler; import android.os.IBinder; import android.os.Process; @@ -58,6 +60,7 @@ import java.util.concurrent.CopyOnWriteArrayList; public class MediaRouter { private static final String TAG = "MediaRouter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean USE_SESSIONS = true; static class Static implements DisplayManager.DisplayListener { final Context mAppContext; @@ -1141,7 +1144,7 @@ public class MediaRouter { public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) { return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable); } - + /** * Create a new route category. Each route must belong to a category. * @@ -1980,6 +1983,7 @@ public class MediaRouter { */ public static class UserRouteInfo extends RouteInfo { RemoteControlClient mRcc; + SessionVolumeProvider mSvp; UserRouteInfo(RouteCategory category) { super(category); @@ -1996,7 +2000,7 @@ public class MediaRouter { mName = name; routeUpdated(); } - + /** * Set the user-visible name of this route. * <p> @@ -2100,7 +2104,11 @@ public class MediaRouter { public void setPlaybackType(int type) { if (mPlaybackType != type) { mPlaybackType = type; - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type); + } } } @@ -2113,8 +2121,12 @@ public class MediaRouter { public void setVolumeHandling(int volumeHandling) { if (mVolumeHandling != volumeHandling) { mVolumeHandling = volumeHandling; - setPlaybackInfoOnRcc( - RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + setPlaybackInfoOnRcc( + RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling); + } } } @@ -2127,7 +2139,13 @@ public class MediaRouter { volume = Math.max(0, Math.min(volume, getVolumeMax())); if (mVolume != volume) { mVolume = volume; - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume); + if (USE_SESSIONS) { + if (mSvp != null) { + mSvp.notifyVolumeChanged(); + } + } else { + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume); + } dispatchRouteVolumeChanged(this); if (mGroup != null) { mGroup.memberVolumeChanged(this); @@ -2166,7 +2184,11 @@ public class MediaRouter { public void setVolumeMax(int volumeMax) { if (mVolumeMax != volumeMax) { mVolumeMax = volumeMax; - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax); + } } } @@ -2177,29 +2199,76 @@ public class MediaRouter { public void setPlaybackStream(int stream) { if (mPlaybackStream != stream) { mPlaybackStream = stream; - setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream); + } } } private void updatePlaybackInfoOnRcc() { - if ((mRcc != null) && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) { - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream); - mRcc.setPlaybackInformation( - RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType); - // let AudioService know whom to call when remote volume needs to be updated - try { - sStatic.mAudioService.registerRemoteVolumeObserverForRcc( - mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */); - } catch (RemoteException e) { - Log.e(TAG, "Error registering remote volume observer", e); + if (USE_SESSIONS) { + configureSessionVolume(); + } else { + if ((mRcc != null) + && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) { + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream); + mRcc.setPlaybackInformation( + RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType); + // let AudioService know whom to call when remote volume + // needs to be updated + try { + sStatic.mAudioService.registerRemoteVolumeObserverForRcc( + mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */); + } catch (RemoteException e) { + Log.e(TAG, "Error registering remote volume observer", e); + } + } + } + } + + private void configureSessionVolume() { + if (mRcc == null) { + if (DEBUG) { + Log.d(TAG, "No Rcc to configure volume for route " + mName); + } + return; + } + MediaSession session = mRcc.getMediaSession(); + if (session == null) { + if (DEBUG) { + Log.d(TAG, "Rcc has no session to configure volume"); + } + return; + } + if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) { + int volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_FIXED; + switch (mVolumeHandling) { + case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE: + volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE; + break; + case RemoteControlClient.PLAYBACK_VOLUME_FIXED: + default: + break; } + // Only register a new listener if necessary + if (mSvp == null || mSvp.getVolumeControl() != volumeControl + || mSvp.getMaxVolume() != mVolumeMax) { + mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax); + session.setPlaybackToRemote(mSvp); + } + } else { + // We only know how to handle local and remote, fall back to local if not remote. + session.setPlaybackToLocal(mPlaybackStream); + mSvp = null; } } @@ -2208,6 +2277,42 @@ public class MediaRouter { mRcc.setPlaybackInformation(what, value); } } + + class SessionVolumeProvider extends RemoteVolumeProvider { + + public SessionVolumeProvider(int volumeControl, int maxVolume) { + super(volumeControl, maxVolume); + } + + @Override + public int onGetCurrentVolume() { + return mVcb == null ? 0 : mVcb.route.mVolume; + } + + @Override + public void onSetVolumeTo(final int volume) { + sStatic.mHandler.post(new Runnable() { + @Override + public void run() { + if (mVcb != null) { + mVcb.vcb.onVolumeSetRequest(mVcb.route, volume); + } + } + }); + } + + @Override + public void onAdjustVolumeBy(final int delta) { + sStatic.mHandler.post(new Runnable() { + @Override + public void run() { + if (mVcb != null) { + mVcb.vcb.onVolumeUpdateRequest(mVcb.route, delta); + } + } + }); + } + } } /** @@ -2504,17 +2609,17 @@ public class MediaRouter { public CharSequence getName() { return getName(sStatic.mResources); } - + /** * Return the properly localized/configuration dependent name of this RouteCategory. - * + * * @param context Context to resolve name resources * @return the name of this route category */ public CharSequence getName(Context context) { return getName(context.getResources()); } - + CharSequence getName(Resources res) { if (mNameResId != 0) { return res.getText(mNameResId); diff --git a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl b/media/java/android/media/session/IActiveSessionsListener.aidl index d281ce6f4702..e5e24bc9f805 100644 --- a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl +++ b/media/java/android/media/session/IActiveSessionsListener.aidl @@ -1,5 +1,4 @@ -/* - * Copyright (C) 2014 The Android Open Source Project +/* 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. @@ -14,16 +13,14 @@ * limitations under the License. */ -package android.hardware.hdmi; +package android.media.session; -import android.hardware.hdmi.HdmiCecMessage; +import android.media.session.MediaSessionToken; /** - * Interface definition for HdmiCecService to do interprocess communcation. - * + * Listens for changes to the list of active sessions. * @hide */ -oneway interface IHdmiCecListener { - void onMessageReceived(in HdmiCecMessage message); - void onCableStatusChanged(in boolean connected); -} +oneway interface IActiveSessionsListener { + void onActiveSessionsChanged(in List<MediaSessionToken> sessions); +}
\ No newline at end of file diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index 9ce06924c261..7c039079796a 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -19,6 +19,7 @@ import android.content.Intent; import android.media.MediaMetadata; import android.media.Rating; import android.media.session.ISessionControllerCallback; +import android.media.session.MediaSessionInfo; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.ResultReceiver; @@ -35,6 +36,7 @@ interface ISessionController { void unregisterCallbackListener(in ISessionControllerCallback cb); boolean isTransportControlEnabled(); void showRoutePicker(); + MediaSessionInfo getSessionInfo(); // These commands are for the TransportController void play(); diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index 6d9888f09c14..bd1fa851fcc2 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -16,6 +16,7 @@ package android.media.session; import android.content.ComponentName; +import android.media.session.IActiveSessionsListener; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.os.Bundle; @@ -30,4 +31,7 @@ interface ISessionManager { List<IBinder> getSessions(in ComponentName compName, int userId); void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock); void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags); + void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, + int userId); + void removeSessionsListener(in IActiveSessionsListener listener); }
\ No newline at end of file diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index caff1ad3e274..57a0a540c781 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -246,6 +246,21 @@ public final class MediaController { } } + /** + * Get the info for the session this controller is connected to. + * + * @return The session info for the connected session. + * @hide + */ + public MediaSessionInfo getSessionInfo() { + try { + return mSessionBinder.getSessionInfo(); + } catch (RemoteException e) { + Log.e(TAG, "Error in getSessionInfo.", e); + } + return null; + } + /* * @hide */ diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 7972639a97bf..4ba1351749a6 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -277,6 +277,7 @@ public final class MediaSession { throw new IllegalArgumentException("volumeProvider may not be null!"); } mVolumeProvider = volumeProvider; + volumeProvider.setSession(this); try { mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(), @@ -522,6 +523,24 @@ public final class MediaSession { } } + /** + * Notify the system that the remove volume changed. + * + * @param provider The provider that is handling volume changes. + * @hide + */ + void notifyRemoteVolumeChanged(RemoteVolumeProvider provider) { + if (provider == null || provider != mVolumeProvider) { + Log.w(TAG, "Received update from stale volume provider"); + return; + } + try { + mBinder.setCurrentVolume(provider.onGetCurrentVolume()); + } catch (RemoteException e) { + Log.e(TAG, "Error in notifyVolumeChanged", e); + } + } + private void dispatchPlay() { postToTransportCallbacks(TransportMessageHandler.MSG_PLAY); } @@ -963,27 +982,26 @@ public final class MediaSession { @Override public void onRouteStateChange(int state) throws RemoteException { // TODO - } - /* - * (non-Javadoc) - * @see android.media.session.ISessionCallback#onAdjustVolumeBy(int) - */ @Override public void onAdjustVolumeBy(int delta) throws RemoteException { - // TODO(epastern): Auto-generated method stub - + MediaSession session = mMediaSession.get(); + if (session != null) { + if (session.mVolumeProvider != null) { + session.mVolumeProvider.onAdjustVolumeBy(delta); + } + } } - /* - * (non-Javadoc) - * @see android.media.session.ISessionCallback#onSetVolumeTo(int) - */ @Override public void onSetVolumeTo(int value) throws RemoteException { - // TODO(epastern): Auto-generated method stub - + MediaSession session = mMediaSession.get(); + if (session != null) { + if (session.mVolumeProvider != null) { + session.mVolumeProvider.onSetVolumeTo(value); + } + } } } diff --git a/media/java/android/media/session/MediaSessionInfo.aidl b/media/java/android/media/session/MediaSessionInfo.aidl new file mode 100644 index 000000000000..63dca9a102b0 --- /dev/null +++ b/media/java/android/media/session/MediaSessionInfo.aidl @@ -0,0 +1,18 @@ +/* Copyright 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.media.session; + +parcelable MediaSessionInfo; diff --git a/media/java/android/media/session/MediaSessionInfo.java b/media/java/android/media/session/MediaSessionInfo.java index f7012119369c..4dc1c09b6aee 100644 --- a/media/java/android/media/session/MediaSessionInfo.java +++ b/media/java/android/media/session/MediaSessionInfo.java @@ -26,18 +26,21 @@ import android.os.Parcelable; public final class MediaSessionInfo implements Parcelable { private final String mId; private final String mPackageName; + private final int mPid; /** * @hide */ - public MediaSessionInfo(String id, String packageName) { + public MediaSessionInfo(String id, String packageName, int pid) { mId = id; mPackageName = packageName; + mPid = pid; } private MediaSessionInfo(Parcel in) { mId = in.readString(); mPackageName = in.readString(); + mPid = in.readInt(); } /** @@ -58,9 +61,13 @@ public final class MediaSessionInfo implements Parcelable { return mId; } + public int getPid() { + return mPid; + } + @Override public String toString() { - return "SessionInfo {id=" + mId + ", pkg=" + mPackageName + "}"; + return "SessionInfo {id=" + mId + ", pkg=" + mPackageName + ", pid=" + mPid + "}"; } @Override @@ -72,6 +79,7 @@ public final class MediaSessionInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(mId); dest.writeString(mPackageName); + dest.writeInt(mPid); } public static final Parcelable.Creator<MediaSessionInfo> CREATOR diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 9e8b0d36b5b2..291bfc8f9de3 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -142,6 +142,50 @@ public final class MediaSessionManager { } /** + * Add a listener to be notified when the list of active sessions + * changes.This requires the + * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by + * the calling app. You may also retrieve this list if your app is an + * enabled notification listener using the + * {@link NotificationListenerService} APIs, in which case you must pass the + * {@link ComponentName} of your enabled listener. + * + * @param sessionListener The listener to add. + * @param notificationListener The enabled notification listener component. + * May be null. + * @param userId The userId to listen for changes on. + * @hide + */ + public void addActiveSessionsListener(SessionListener sessionListener, + ComponentName notificationListener, int userId) { + if (sessionListener == null) { + throw new IllegalArgumentException("listener may not be null"); + } + try { + mService.addSessionsListener(sessionListener.mStub, notificationListener, userId); + } catch (RemoteException e) { + Log.e(TAG, "Error in addActiveSessionsListener.", e); + } + } + + /** + * Stop receiving active sessions updates on the specified listener. + * + * @param listener The listener to remove. + * @hide + */ + public void removeActiveSessionsListener(SessionListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener may not be null"); + } + try { + mService.removeSessionsListener(listener.mStub); + } catch (RemoteException e) { + Log.e(TAG, "Error in removeActiveSessionsListener.", e); + } + } + + /** * Send a media key event. The receiver will be selected automatically. * * @param keyEvent The KeyEvent to send. @@ -184,4 +228,35 @@ public final class MediaSessionManager { Log.e(TAG, "Failed to send adjust volume.", e); } } + + /** + * Listens for changes to the list of active sessions. This can be added + * using {@link #addActiveSessionsListener}. + * + * @hide + */ + public static abstract class SessionListener { + /** + * Called when the list of active sessions has changed. This can be due + * to a session being added or removed or the order of sessions + * changing. + * + * @param controllers The updated list of controllers for the user that + * changed. + */ + public abstract void onActiveSessionsChanged(List<MediaController> controllers); + + private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() { + @Override + public void onActiveSessionsChanged(List<MediaSessionToken> tokens) + throws RemoteException { + ArrayList<MediaController> controllers = new ArrayList<MediaController>(); + int size = tokens.size(); + for (int i = 0; i < size; i++) { + controllers.add(MediaController.fromToken(tokens.get(i))); + } + SessionListener.this.onActiveSessionsChanged(controllers); + } + }; + } } diff --git a/media/java/android/media/session/MediaSessionToken.java b/media/java/android/media/session/MediaSessionToken.java index 86f566203c6d..e5991894728b 100644 --- a/media/java/android/media/session/MediaSessionToken.java +++ b/media/java/android/media/session/MediaSessionToken.java @@ -31,7 +31,7 @@ public final class MediaSessionToken implements Parcelable { /** * @hide */ - MediaSessionToken(ISessionController binder) { + public MediaSessionToken(ISessionController binder) { mBinder = binder; } diff --git a/media/java/android/media/session/RemoteVolumeProvider.java b/media/java/android/media/session/RemoteVolumeProvider.java index 47f672f37da8..606b1d796ec9 100644 --- a/media/java/android/media/session/RemoteVolumeProvider.java +++ b/media/java/android/media/session/RemoteVolumeProvider.java @@ -15,6 +15,9 @@ */ package android.media.session; +import android.os.RemoteException; +import android.util.Log; + /** * Handles requests to adjust or set the volume on a session. This is also used * to push volume updates back to the session after a request has been handled. @@ -22,6 +25,7 @@ package android.media.session; * {@link MediaSession#setPlaybackToRemote}. */ public abstract class RemoteVolumeProvider { + private static final String TAG = "RemoteVolumeProvider"; /** * The volume is fixed and can not be modified. Requests to change volume @@ -46,6 +50,8 @@ public abstract class RemoteVolumeProvider { private final int mControlType; private final int mMaxVolume; + private MediaSession mSession; + /** * Create a new volume provider for handling volume events. You must specify * the type of volume control and the maximum volume that can be used. @@ -88,7 +94,7 @@ public abstract class RemoteVolumeProvider { * Notify the system that the remote playback's volume has been changed. */ public final void notifyVolumeChanged() { - // TODO + mSession.notifyRemoteVolumeChanged(this); } /** @@ -107,4 +113,11 @@ public abstract class RemoteVolumeProvider { */ public void onAdjustVolumeBy(int delta) { } + + /** + * @hide + */ + void setSession(MediaSession session) { + mSession = session; + } }
\ No newline at end of file diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 5e650c2373f6..52045d374b73 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -306,7 +306,7 @@ public final class TvContract { public static final int TYPE_ATSC_C = 0x00030200; /** The channel type for ATSC-M/H (mobile/handheld). */ - public static final int TYPE_ATSC_M_H = 0x00030200; + public static final int TYPE_ATSC_M_H = 0x00030300; /** The channel type for ISDB-T (terrestrial). */ public static final int TYPE_ISDB_T = 0x00040000; diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 9525c081bec6..868c5bf09081 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -46,6 +46,27 @@ public final class TvInputInfo implements Parcelable { private static final String TAG = "TvInputInfo"; /** + * TV input type: the TV input service is not handling input from hardware. For example, + * services showing streaming from the internet falls into this type. + */ + public static final int TYPE_VIRTUAL = 0; + + // Should be in sync with hardware/libhardware/include/hardware/tv_input.h + + /** + * TV input type: the TV input service is HDMI. (e.g. HDMI 1) + */ + public static final int TYPE_HDMI = 1; + /** + * TV input type: the TV input service is a tuner. (e.g. terrestrial tuner) + */ + public static final int TYPE_TUNER = 2; + /** + * TV input type: the TV input service is stateless pass-through. (e.g. RGB, composite, etc.) + */ + public static final int TYPE_PASSTHROUGH = 3; + + /** * The name of the TV input service to provide to the setup activity and settings activity. */ public static final String EXTRA_SERVICE_NAME = "serviceName"; @@ -58,6 +79,7 @@ public final class TvInputInfo implements Parcelable { // Attributes from XML meta data. private String mSetupActivity; private String mSettingsActivity; + private int mType; /** * Create a new instance of the TvInputInfo class, @@ -105,6 +127,11 @@ public final class TvInputInfo implements Parcelable { Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for " + si.name); } + input.mType = sa.getInt( + com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL); + if (DEBUG) { + Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name); + } sa.recycle(); return input; @@ -179,6 +206,13 @@ public final class TvInputInfo implements Parcelable { } /** + * Returns the type of this TV input service. + */ + public int getType() { + return mType; + } + + /** * Loads the user-displayed label for this TV input service. * * @param pm Supplies a PackageManager used to load the TV input's resources. diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 245332507ac6..2831d9e45d6b 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -33,12 +33,13 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.ViewGroup; import android.view.ViewRootImpl; /** * View playing TV */ -public class TvView extends SurfaceView { +public class TvView extends ViewGroup { private static final String TAG = "TvView"; // STOPSHIP: Turn debugging off. private static final boolean DEBUG = true; @@ -57,6 +58,7 @@ public class TvView extends SurfaceView { private final Handler mHandler = new Handler(); private TvInputManager.Session mSession; + private final SurfaceView mSurfaceView; private Surface mSurface; private boolean mOverlayViewCreated; private Rect mOverlayViewFrame; @@ -124,7 +126,14 @@ public class TvView extends SurfaceView { public TvView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - getHolder().addCallback(mSurfaceHolderCallback); + mSurfaceView = new SurfaceView(context, attrs, defStyleAttr) { + @Override + protected void updateWindow(boolean force, boolean redrawNeeded) { + super.updateWindow(force, redrawNeeded); + relayoutSessionOverlayView(); + }}; + mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback); + addView(mSurfaceView); mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); } @@ -310,11 +319,26 @@ public class TvView extends SurfaceView { super.onDetachedFromWindow(); } - /** @hide */ @Override - protected void updateWindow(boolean force, boolean redrawNeeded) { - super.updateWindow(force, redrawNeeded); - relayoutSessionOverlayView(); + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + mSurfaceView.layout(0, 0, right - left, bottom - top); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec); + int width = mSurfaceView.getMeasuredWidth(); + int height = mSurfaceView.getMeasuredHeight(); + int childState = mSurfaceView.getMeasuredState(); + setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState), + resolveSizeAndState(height, heightMeasureSpec, + childState << MEASURED_HEIGHT_STATE_SHIFT)); + } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + mSurfaceView.setVisibility(visibility); } private void release() { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 286921ecff33..81a1b943a2b2 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -2448,7 +2448,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { } private String getDefaultDeviceName() { - return mContext.getResources().getString(R.string.def_device_name, Build.BRAND, + return mContext.getResources().getString(R.string.def_device_name, Build.MANUFACTURER, Build.MODEL); } } diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index eaa255863395..aa62daa22a19 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -111,47 +111,13 @@ /> </LinearLayout> </LinearLayout> - - <LinearLayout android:id="@+id/ticker" + + <ViewStub + android:id="@+id/ticker_stub" + android:inflatedId="@+id/ticker" + android:layout="@layout/status_bar_ticker" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingStart="6dip" - android:animationCache="false" - android:orientation="horizontal" > - <ImageSwitcher android:id="@+id/tickerIcon" - android:layout_width="@dimen/status_bar_icon_size" - android:layout_height="@dimen/status_bar_icon_size" - android:layout_marginEnd="4dip" - > - <com.android.systemui.statusbar.AnimatedImageView - android:layout_width="@dimen/status_bar_icon_size" - android:layout_height="@dimen/status_bar_icon_size" - android:scaleType="center" - /> - <com.android.systemui.statusbar.AnimatedImageView - android:layout_width="@dimen/status_bar_icon_size" - android:layout_height="@dimen/status_bar_icon_size" - android:scaleType="center" - /> - </ImageSwitcher> - <com.android.systemui.statusbar.phone.TickerView android:id="@+id/tickerText" - android:layout_width="0dip" - android:layout_weight="1" - android:layout_height="wrap_content" - android:paddingTop="2dip" - android:paddingEnd="10dip"> - <TextView - android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - /> - <TextView - android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - /> - </com.android.systemui.statusbar.phone.TickerView> - </LinearLayout> + /> + </com.android.systemui.statusbar.phone.PhoneStatusBarView> diff --git a/packages/SystemUI/res/layout/status_bar_ticker.xml b/packages/SystemUI/res/layout/status_bar_ticker.xml new file mode 100644 index 000000000000..dd9b3efe5f43 --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_ticker.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + - Copyright 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. + --> +<LinearLayout android:id="@+id/ticker" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingStart="6dip" + android:animationCache="false" + android:orientation="horizontal"> + + <ImageSwitcher android:id="@+id/tickerIcon" + android:layout_width="@dimen/status_bar_icon_size" + android:layout_height="@dimen/status_bar_icon_size" + android:layout_marginEnd="4dip" + > + <com.android.systemui.statusbar.AnimatedImageView + android:layout_width="@dimen/status_bar_icon_size" + android:layout_height="@dimen/status_bar_icon_size" + android:scaleType="center" + /> + <com.android.systemui.statusbar.AnimatedImageView + android:layout_width="@dimen/status_bar_icon_size" + android:layout_height="@dimen/status_bar_icon_size" + android:scaleType="center" + /> + </ImageSwitcher> + <com.android.systemui.statusbar.phone.TickerView android:id="@+id/tickerText" + android:layout_width="0dip" + android:layout_weight="1" + android:layout_height="wrap_content" + android:paddingTop="2dip" + android:paddingEnd="10dip"> + <TextView + android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + /> + <TextView + android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + /> + </com.android.systemui.statusbar.phone.TickerView> +</LinearLayout> + diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 79a1df41d2a8..1ef5bcd128bd 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -141,5 +141,10 @@ <!-- Wait on the touch feedback this long before performing an action. --> <integer name="feedback_start_delay">300</integer> + + <!-- Set to true to enable the classic notification ticker that scrolls + Notification.tickerText across the status bar for what seems like an + eternity. --> + <bool name="enable_ticker">false</bool> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index ac9866cadfaa..4749b9ce3a97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -81,6 +81,7 @@ import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewPropertyAnimator; +import android.view.ViewStub; import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.animation.AccelerateInterpolator; @@ -289,6 +290,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, int mTrackingPosition; // the position of the top of the tracking view. // ticker + private boolean mTickerEnabled; private Ticker mTicker; private View mTickerView; private boolean mTicking; @@ -644,7 +646,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon); mNotificationIcons.setOverflowIndicator(mMoreIcon); mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents); - mTickerView = mStatusBarView.findViewById(R.id.ticker); mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( R.id.notification_stack_scroller); @@ -684,10 +685,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mDateTimeView.setEnabled(true); } - mTicker = new MyTicker(context, mStatusBarView); + mTickerEnabled = res.getBoolean(R.bool.enable_ticker); + if (mTickerEnabled) { + final ViewStub tickerStub = (ViewStub) mStatusBarView.findViewById(R.id.ticker_stub); + if (tickerStub != null) { + mTickerView = tickerStub.inflate(); + mTicker = new MyTicker(context, mStatusBarView); - TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText); - tickerView.mTicker = mTicker; + TickerView tickerView = (TickerView) mStatusBarView.findViewById(R.id.tickerText); + tickerView.mTicker = mTicker; + } + } mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); @@ -1145,7 +1153,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (old != null) { // Cancel the ticker if it's still running - mTicker.removeEntry(old); + if (mTickerEnabled) { + mTicker.removeEntry(old); + } // Recalculate the position of the sliding windows and the titles. updateExpandedViewPos(EXPANDED_LEAVE_ALONE); @@ -2118,6 +2128,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override protected void tick(StatusBarNotification n, boolean firstTime) { + if (!mTickerEnabled) return; + // no ticking in lights-out mode if (!areLightsOn()) return; @@ -2134,7 +2146,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (n.getNotification().tickerText != null && mStatusBarWindow != null && mStatusBarWindow.getWindowToken() != null) { if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS - | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { + | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { mTicker.addEntry(n); } } @@ -2143,10 +2155,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private class MyTicker extends Ticker { MyTicker(Context context, View sb) { super(context, sb); + if (!mTickerEnabled) { + Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable()); + } } @Override public void tickerStarting() { + if (!mTickerEnabled) return; mTicking = true; mStatusBarContents.setVisibility(View.GONE); mTickerView.setVisibility(View.VISIBLE); @@ -2156,6 +2172,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void tickerDone() { + if (!mTickerEnabled) return; mStatusBarContents.setVisibility(View.VISIBLE); mTickerView.setVisibility(View.GONE); mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); @@ -2164,6 +2181,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void tickerHalting() { + if (!mTickerEnabled) return; if (mStatusBarContents.getVisibility() != View.VISIBLE) { mStatusBarContents.setVisibility(View.VISIBLE); mStatusBarContents @@ -2202,11 +2220,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, pw.println("Current Status Bar state:"); pw.println(" mExpandedVisible=" + mExpandedVisible + ", mTrackingPosition=" + mTrackingPosition); - pw.println(" mTicking=" + mTicking); + pw.println(" mTickerEnabled=" + mTickerEnabled); + if (mTickerEnabled) { + pw.println(" mTicking=" + mTicking); + pw.println(" mTickerView: " + viewInfo(mTickerView)); + } pw.println(" mTracking=" + mTracking); pw.println(" mDisplayMetrics=" + mDisplayMetrics); pw.println(" mStackScroller: " + viewInfo(mStackScroller)); - pw.println(" mTickerView: " + viewInfo(mTickerView)); pw.println(" mStackScroller: " + viewInfo(mStackScroller) + " scroll " + mStackScroller.getScrollX() + "," + mStackScroller.getScrollY()); @@ -2676,7 +2697,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override protected void haltTicker() { - mTicker.halt(); + if (mTickerEnabled) { + mTicker.halt(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java index 8aa3837ea2ad..bf1375134fff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java @@ -32,7 +32,7 @@ public class TickerView extends TextSwitcher @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - mTicker.reflowText(); + if (mTicker != null) mTicker.reflowText(); } public void setTicker(Ticker t) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java deleted file mode 100644 index baae1d99f9fc..000000000000 --- a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.hdmi; - -import android.hardware.hdmi.HdmiCec; -import android.hardware.hdmi.HdmiCecMessage; -import android.hardware.hdmi.IHdmiCecListener; -import android.os.Binder; -import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * HdmiCecDevice class represents a CEC logical device characterized - * by its device type. It is a superclass of those serving concrete device type. - * Currently we're interested in playback(one of sources), display(sink) device type - * only. The support for the other types like recorder, audio system will come later. - * - * <p>A physical device can contain the functions of - * more than one logical device, in which case it should create - * as many logical devices as necessary. - * - * <p>Note that if a physical device has multiple instances of a particular - * functionality, it should advertize only one instance. For instance, if - * a device has multiple tuners, it should only expose one for control - * via CEC. In this case, it is up to the device itself to manage multiple tuners. - * - * <p>The version of HDMI-CEC protocol supported in this class is 1.3a. - * - * <p>Declared as package-private, accessed by HdmiCecService only. - */ -abstract class HdmiCecDevice { - private static final String TAG = "HdmiCecDevice"; - - private final int mType; - - // List of listeners to the message/event coming to the device. - private final List<IHdmiCecListener> mListeners = new ArrayList<IHdmiCecListener>(); - private final Binder mBinder = new Binder(); - private final HdmiCecService mService; - - private boolean mIsActiveSource; - - /** - * Factory method that creates HdmiCecDevice instance to the device type. - */ - public static HdmiCecDevice create(HdmiCecService service, int type) { - if (type == HdmiCec.DEVICE_PLAYBACK) { - return new HdmiCecDevicePlayback(service, type); - } else if (type == HdmiCec.DEVICE_TV) { - return new HdmiCecDeviceTv(service, type); - } - return null; - } - - /** - * Constructor. - */ - public HdmiCecDevice(HdmiCecService service, int type) { - mService = service; - mType = type; - mIsActiveSource = false; - } - - /** - * Called right after the class is instantiated. This method can be used to - * implement any initialization tasks for the instance. - */ - abstract public void initialize(); - - /** - * Return the binder token that identifies this instance. - */ - public Binder getToken() { - return mBinder; - } - - /** - * Return the service instance. - */ - public HdmiCecService getService() { - return mService; - } - - /** - * Return the type of this device. - */ - public int getType() { - return mType; - } - - /** - * Register a listener to be invoked when events occur. - * - * @param listener the listern that will run - */ - public void addListener(IHdmiCecListener listener) { - mListeners.add(listener); - } - - /** - * Remove the listener that was previously registered. - * - * @param listener IHdmiCecListener instance to be removed - */ - public void removeListener(IHdmiCecListener listener) { - mListeners.remove(listener); - } - - /** - * Indicate if the device has listeners. - * - * @return true if there are listener instances for this device - */ - public boolean hasListener() { - return !mListeners.isEmpty(); - } - - /** - * Handle HDMI-CEC message coming to the device by invoking the registered - * listeners. - */ - public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) { - if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE) { - mIsActiveSource = false; - } - - if (mListeners.size() == 0) { - return; - } - HdmiCecMessage message = new HdmiCecMessage(srcAddress, dstAddress, opcode, params); - for (IHdmiCecListener listener : mListeners) { - try { - listener.onMessageReceived(message); - } catch (RemoteException e) { - Log.e(TAG, "listener.onMessageReceived failed."); - } - } - } - - public void handleHotplug(boolean connected) { - for (IHdmiCecListener listener : mListeners) { - try { - listener.onCableStatusChanged(connected); - } catch (RemoteException e) { - Log.e(TAG, "listener.onCableStatusChanged failed."); - } - } - } - - /** - * Return the active status of the device. - * - * @return true if the device is the active source among the connected - * HDMI-CEC-enabled devices; otherwise false. - */ - public boolean isActiveSource() { - return mIsActiveSource; - } - - /** - * Update the active source state of the device. - */ - public void setIsActiveSource(boolean state) { - mIsActiveSource = state; - } - - /** - * Send <Active Source> command. The default implementation does nothing. Should be - * overriden by subclass. - */ - public void sendActiveSource(int physicalAddress) { - logWarning("<Active Source> not valid for the device type: " + mType - + " address:" + physicalAddress); - } - - /** - * Send <Inactive Source> command. The default implementation does nothing. Should be - * overriden by subclass. - */ - public void sendInactiveSource(int physicalAddress) { - logWarning("<Inactive Source> not valid for the device type: " + mType - + " address:" + physicalAddress); - } - - /** - * Send <Image View On> command. The default implementation does nothing. Should be - * overriden by subclass. - */ - public void sendImageViewOn() { - logWarning("<Image View On> not valid for the device type: " + mType); - } - - /** - * Send <Text View On> command. The default implementation does nothing. Should be - * overriden by subclass. - */ - public void sendTextViewOn() { - logWarning("<Text View On> not valid for the device type: " + mType); - } - - /** - * Check if the connected sink device is in powered-on state. The default implementation - * simply returns false. Should be overriden by subclass to report the correct state. - */ - public boolean isSinkDeviceOn() { - logWarning("isSinkDeviceOn() not valid for the device type: " + mType); - return false; - } - - private void logWarning(String msg) { - Log.w(TAG, msg); - } -} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java deleted file mode 100644 index f8cf11dbf92f..000000000000 --- a/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.hdmi; - -import android.hardware.hdmi.HdmiCec; - -/** - * Class for the logical device of playback type. Devices such as DVD/Blueray player - * that support 'playback' feature are classified as playback device. It is common - * that they don't have built-in display, therefore need to talk, stream their contents - * to TV/display device which is connected through HDMI cable. - * - * <p>It closely monitors the status of display device (other devices can be of interest - * too, but with much less priority), declares itself as 'active source' to have - * display show its output, switch the source state as ordered by display that may be - * talking to many other devices connected to it. It also receives commands from display - * such as remote control signal, standby, status report, playback mode. - * - * <p>Declared as package-private, accessed by HdmiCecService only. - */ -final class HdmiCecDevicePlayback extends HdmiCecDevice { - private static final String TAG = "HdmiCecDevicePlayback"; - - private int mSinkDevicePowerStatus; - - /** - * Constructor. - */ - public HdmiCecDevicePlayback(HdmiCecService service, int type) { - super(service, type); - mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_UNKNOWN; - } - - @Override - public void initialize() { - // Playback device tries to obtain the power status of TV/display when created, - // and maintains it all through its lifecycle. CEC spec says there is - // a maximum 1 second response time. Therefore it should be kept in mind - // that there can be as much amount of period of time the power status - // of the display remains unknown after the query is sent out. - queryTvPowerStatus(); - } - - private void queryTvPowerStatus() { - getService().sendMessage(getType(), HdmiCec.ADDR_TV, - HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS, HdmiCecService.EMPTY_PARAM); - } - - @Override - public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) { - // Updates power status of display. The cases are: - // 1) Response for the queried power status request arrives. Update the status. - // 2) Broadcast or direct <Standby> command from TV, which is sent as TV itself is going - // into standby mode too. - if (opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS) { - mSinkDevicePowerStatus = params[0]; - } else if (srcAddress == HdmiCec.ADDR_TV) { - if (opcode == HdmiCec.MESSAGE_STANDBY) { - mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_STANDBY; - } - } - super.handleMessage(srcAddress, dstAddress, opcode, params); - } - - @Override - public void handleHotplug(boolean connected) { - // If cable get disconnected sink device becomes unreachable. Switch the status - // to unknown, and query the status once the cable gets connected back. - if (!connected) { - mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_UNKNOWN; - } else { - queryTvPowerStatus(); - } - super.handleHotplug(connected); - } - - @Override - public boolean isSinkDeviceOn() { - return mSinkDevicePowerStatus == HdmiCec.POWER_STATUS_ON; - } - - @Override - public void sendActiveSource(int physicalAddress) { - setIsActiveSource(true); - byte[] param = new byte[] { - (byte) ((physicalAddress >> 8) & 0xff), - (byte) (physicalAddress & 0xff) - }; - getService().sendMessage(getType(), HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_ACTIVE_SOURCE, - param); - } - - @Override - public void sendInactiveSource(int physicalAddress) { - setIsActiveSource(false); - byte[] param = new byte[] { - (byte) ((physicalAddress >> 8) & 0xff), - (byte) (physicalAddress & 0xff) - }; - getService().sendMessage(getType(), HdmiCec.ADDR_TV, HdmiCec.MESSAGE_INACTIVE_SOURCE, - param); - } - - @Override - public void sendImageViewOn() { - getService().sendMessage(getType(), HdmiCec.ADDR_TV, HdmiCec.MESSAGE_IMAGE_VIEW_ON, - HdmiCecService.EMPTY_PARAM); - } - - @Override - public void sendTextViewOn() { - getService().sendMessage(getType(), HdmiCec.ADDR_TV, HdmiCec.MESSAGE_TEXT_VIEW_ON, - HdmiCecService.EMPTY_PARAM); - } -} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java deleted file mode 100644 index 09ff3ca75d6b..000000000000 --- a/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.hdmi; - -/** - * Class for logical device of TV type. - */ -final class HdmiCecDeviceTv extends HdmiCecDevice { - private static final String TAG = "HdmiCecDeviceTv"; - - /** - * Constructor. - */ - public HdmiCecDeviceTv(HdmiCecService service, int type) { - super(service, type); - } - - public void initialize() { - // TODO: Do the initialization task for TV device here. - } -} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecService.java b/services/core/java/com/android/server/hdmi/HdmiCecService.java deleted file mode 100644 index 98dc72fa7446..000000000000 --- a/services/core/java/com/android/server/hdmi/HdmiCecService.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.hdmi; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.hardware.hdmi.HdmiCec; -import android.hardware.hdmi.HdmiCecMessage; -import android.hardware.hdmi.IHdmiCecListener; -import android.hardware.hdmi.IHdmiCecService; -import android.os.Binder; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.text.TextUtils; -import android.util.Log; -import android.util.SparseArray; - -import com.android.server.SystemService; -import libcore.util.EmptyArray; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Locale; - -/** - * Provides a service for sending and processing HDMI-CEC messages, and providing - * the information on HDMI settings in general. - */ -public final class HdmiCecService extends SystemService { - private static final String TAG = "HdmiCecService"; - - // Maintains the allocated logical devices. Device type, not logical address, - // is used for key as logical address is likely to change over time while - // device type is permanent. Type-address mapping is maintained only at - // native level. - private final SparseArray<HdmiCecDevice> mLogicalDevices = new SparseArray<HdmiCecDevice>(); - - // List of IBinder.DeathRecipient instances to handle dead IHdmiCecListener - // objects. - private final ArrayList<ListenerRecord> mListenerRecords = new ArrayList<ListenerRecord>(); - - // Used to synchronize the access to the service. - private final Object mLock = new Object(); - - // Stores the pointer to the native implementation of the service that - // interacts with HAL. - private long mNativePtr; - - private static final String PERMISSION = "android.permission.HDMI_CEC"; - - static final byte[] EMPTY_PARAM = EmptyArray.BYTE; - - public HdmiCecService(Context context) { - super(context); - } - - private static native long nativeInit(HdmiCecService service); - - @Override - public void onStart() { - // Stop publishing the service. Soon to be deprecated. - Log.w(TAG, "In transition to HdmiControlService. May not work."); - } - - /** - * Called by native when an HDMI-CEC message arrived. Invokes the registered - * listeners to handle the message. - */ - private void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) { - // TODO: Messages like <Standby> may not need be passed to listener - // but better be handled in service by turning off the screen - // or putting the device into suspend mode. List up such messages - // and handle them here. - synchronized (mLock) { - if (dstAddress == HdmiCec.ADDR_BROADCAST) { - for (int i = 0; i < mLogicalDevices.size(); ++i) { - mLogicalDevices.valueAt(i).handleMessage(srcAddress, dstAddress, opcode, - params); - } - } else { - int type = HdmiCec.getTypeFromAddress(dstAddress); - HdmiCecDevice device = mLogicalDevices.get(type); - if (device == null) { - Log.w(TAG, "logical device not found. type: " + type); - return; - } - device.handleMessage(srcAddress, dstAddress, opcode, params); - } - } - } - - /** - * Called by native when internal HDMI hotplug event occurs. Invokes the registered - * listeners to handle the event. - */ - private void handleHotplug(boolean connected) { - synchronized(mLock) { - for (int i = 0; i < mLogicalDevices.size(); ++i) { - mLogicalDevices.valueAt(i).handleHotplug(connected); - } - } - } - - /** - * Called by native when it needs to know whether we have an active source. - * The native part uses the return value to respond to <Request Active - * Source >. - * - * @return type of the device which is active; DEVICE_INACTIVE if there is - * no active logical device in the system. - */ - private int getActiveSource() { - synchronized(mLock) { - for (int i = 0; i < mLogicalDevices.size(); ++i) { - if (mLogicalDevices.valueAt(i).isActiveSource()) { - return mLogicalDevices.keyAt(i); - } - } - } - return HdmiCec.DEVICE_INACTIVE; - } - - /** - * Called by native when a request for the menu language of the device was - * received. The native part uses the return value to generate the message - * <Set Menu Language> in response. The language should be of - * the 3-letter format as defined in ISO/FDIS 639-2. We use system default - * locale. - */ - private String getLanguage(int type) { - return Locale.getDefault().getISO3Language(); - } - - private void enforceAccessPermission() { - getContext().enforceCallingOrSelfPermission(PERMISSION, "HdmiCecService"); - } - - private void dumpInternal(PrintWriter pw) { - pw.println("HdmiCecService (dumpsys hdmi_cec)"); - pw.println(""); - synchronized (mLock) { - for (int i = 0; i < mLogicalDevices.size(); ++i) { - HdmiCecDevice device = mLogicalDevices.valueAt(i); - pw.println("Device: type=" + device.getType() + - ", active=" + device.isActiveSource()); - } - } - } - - // Remove logical device of a given type. - private void removeLogicalDeviceLocked(int type) { - ensureValidType(type); - mLogicalDevices.remove(type); - nativeRemoveLogicalAddress(mNativePtr, type); - } - - private static void ensureValidType(int type) { - if (!HdmiCec.isValidType(type)) { - throw new IllegalArgumentException("invalid type: " + type); - } - } - - // Return the logical device identified by the given binder token. - private HdmiCecDevice getLogicalDeviceLocked(IBinder b) { - for (int i = 0; i < mLogicalDevices.size(); ++i) { - HdmiCecDevice device = mLogicalDevices.valueAt(i); - if (device.getToken() == b) { - return device; - } - } - throw new IllegalArgumentException("Device not found"); - } - - // package-private. Used by HdmiCecDevice and its subclasses only. - void sendMessage(int type, int address, int opcode, byte[] params) { - nativeSendMessage(mNativePtr, type, address, opcode, params); - } - - private void setOsdNameLocked(String name) { - nativeSetOsdName(mNativePtr, name.getBytes(Charset.forName("US-ASCII"))); - } - - private final class ListenerRecord implements IBinder.DeathRecipient { - private final IHdmiCecListener mListener; - private final int mType; - - public ListenerRecord(IHdmiCecListener listener, int type) { - mListener = listener; - mType = type; - } - - @Override - public void binderDied() { - synchronized (mLock) { - mListenerRecords.remove(this); - HdmiCecDevice device = mLogicalDevices.get(mType); - if (device != null) { - device.removeListener(mListener); - if (!device.hasListener()) { - removeLogicalDeviceLocked(mType); - } - } - } - } - } - - private final class BinderService extends IHdmiCecService.Stub { - - @Override - public IBinder allocateLogicalDevice(int type, IHdmiCecListener listener) { - enforceAccessPermission(); - ensureValidType(type); - if (listener == null) { - throw new IllegalArgumentException("listener must not be null"); - } - synchronized (mLock) { - HdmiCecDevice device = mLogicalDevices.get(type); - if (device != null) { - Log.v(TAG, "Logical address already allocated. Adding listener only."); - } else { - int address = nativeAllocateLogicalAddress(mNativePtr, type); - if (!HdmiCec.isValidAddress(address)) { - Log.e(TAG, "Logical address was not allocated"); - return null; - } else { - device = HdmiCecDevice.create(HdmiCecService.this, type); - if (device == null) { - Log.e(TAG, "Device type not supported yet."); - return null; - } - device.initialize(); - mLogicalDevices.put(type, device); - } - } - - // Adds the listener and its monitor - ListenerRecord record = new ListenerRecord(listener, type); - try { - listener.asBinder().linkToDeath(record, 0); - } catch (RemoteException e) { - Log.w(TAG, "Listener already died"); - if (!device.hasListener()) { - removeLogicalDeviceLocked(type); - } - return null; - } - mListenerRecords.add(record); - device.addListener(listener); - return device.getToken(); - } - } - - @Override - public void sendActiveSource(IBinder b) { - enforceAccessPermission(); - synchronized (mLock) { - HdmiCecDevice device = getLogicalDeviceLocked(b); - device.sendActiveSource(nativeGetPhysicalAddress(mNativePtr)); - } - } - - @Override - public void sendInactiveSource(IBinder b) { - enforceAccessPermission(); - synchronized (mLock) { - HdmiCecDevice device = getLogicalDeviceLocked(b); - device.sendInactiveSource(nativeGetPhysicalAddress(mNativePtr)); - } - } - - @Override - public void sendImageViewOn(IBinder b) { - enforceAccessPermission(); - synchronized (mLock) { - HdmiCecDevice device = getLogicalDeviceLocked(b); - device.sendImageViewOn(); - } - } - - @Override - public void sendTextViewOn(IBinder b) { - enforceAccessPermission(); - synchronized (mLock) { - HdmiCecDevice device = getLogicalDeviceLocked(b); - device.sendTextViewOn(); - } - } - - public void sendGiveDevicePowerStatus(IBinder b, int address) { - enforceAccessPermission(); - synchronized (mLock) { - HdmiCecDevice device = getLogicalDeviceLocked(b); - nativeSendMessage(mNativePtr, device.getType(), address, - HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS, EMPTY_PARAM); - } - } - - @Override - public boolean isTvOn(IBinder b) { - enforceAccessPermission(); - synchronized (mLock) { - HdmiCecDevice device = getLogicalDeviceLocked(b); - return device.isSinkDeviceOn(); - } - } - - @Override - public void removeServiceListener(IBinder b, IHdmiCecListener listener) { - enforceAccessPermission(); - if (listener == null) { - throw new IllegalArgumentException("listener must not be null"); - } - synchronized (mLock) { - HdmiCecDevice device = getLogicalDeviceLocked(b); - for (ListenerRecord record : mListenerRecords) { - if (record.mType == device.getType() - && record.mListener.asBinder() == listener.asBinder()) { - mListenerRecords.remove(record); - device.removeListener(record.mListener); - if (!device.hasListener()) { - removeLogicalDeviceLocked(record.mType); - } - break; - } - } - } - } - - @Override - public void sendMessage(IBinder b, HdmiCecMessage message) { - enforceAccessPermission(); - if (message == null) { - throw new IllegalArgumentException("message must not be null"); - } - synchronized (mLock) { - HdmiCecDevice device = getLogicalDeviceLocked(b); - nativeSendMessage(mNativePtr, device.getType(), message.getDestination(), - message.getOpcode(), message.getParams()); - } - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission denial: can't dump HdmiCecService from pid=" - + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " without permission " + android.Manifest.permission.DUMP); - return; - } - final long ident = Binder.clearCallingIdentity(); - try { - dumpInternal(pw); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - private static native int nativeAllocateLogicalAddress(long handler, int deviceType); - private static native void nativeRemoveLogicalAddress(long handler, int deviceType); - private static native void nativeSendMessage(long handler, int deviceType, int destination, - int opcode, byte[] params); - private static native int nativeGetPhysicalAddress(long handler); - private static native void nativeSetOsdName(long handler, byte[] name); -} diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 737ffda77c93..835b0941abc2 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -130,7 +130,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mOwnerPid = ownerPid; mOwnerUid = ownerUid; mUserId = userId; - mSessionInfo = new MediaSessionInfo(UUID.randomUUID().toString(), ownerPackageName); + mSessionInfo = new MediaSessionInfo(UUID.randomUUID().toString(), ownerPackageName, + ownerPid); mTag = tag; mController = new ControllerStub(); mSession = new SessionStub(); @@ -943,6 +944,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override + public MediaSessionInfo getSessionInfo() { + return mSessionInfo; + } + + @Override public void play() throws RemoteException { mSessionCb.play(); } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 87665e103fcc..67065ba33a86 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -29,9 +29,11 @@ import android.content.pm.PackageManager; import android.media.AudioManager; import android.media.IAudioService; import android.media.routeprovider.RouteRequest; +import android.media.session.IActiveSessionsListener; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.media.session.ISessionManager; +import android.media.session.MediaSessionToken; import android.media.session.RouteInfo; import android.media.session.RouteOptions; import android.media.session.MediaSession; @@ -39,6 +41,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.ResultReceiver; @@ -75,10 +78,12 @@ public class MediaSessionService extends SystemService implements Monitor { private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>(); private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>(); + private final ArrayList<SessionsListenerRecord> mSessionsListeners + = new ArrayList<SessionsListenerRecord>(); // private final ArrayList<MediaRouteProviderProxy> mProviders // = new ArrayList<MediaRouteProviderProxy>(); private final Object mLock = new Object(); - private final Handler mHandler = new Handler(); + private final MessageHandler mHandler = new MessageHandler(); private final PowerManager.WakeLock mMediaEventWakeLock; private KeyguardManager mKeyguardManager; @@ -200,15 +205,20 @@ public class MediaSessionService extends SystemService implements Monitor { } } } + mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0); } public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) { + boolean updateSessions = false; synchronized (mLock) { if (!mAllSessions.contains(record)) { Log.d(TAG, "Unknown session changed playback state. Ignoring."); return; } - mPriorityStack.onPlaystateChange(record, oldState, newState); + updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState); + } + if (updateSessions) { + mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0); } } @@ -315,6 +325,8 @@ public class MediaSessionService extends SystemService implements Monitor { // ignore exceptions while destroying a session. } session.onDestroy(); + + mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0); } private void enforcePackageName(String packageName, int uid) { @@ -428,6 +440,8 @@ public class MediaSessionService extends SystemService implements Monitor { UserRecord user = getOrCreateUser(userId); user.addSessionLocked(session); + mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0); + if (DEBUG) { Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag); } @@ -453,11 +467,43 @@ public class MediaSessionService extends SystemService implements Monitor { return -1; } + private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { + for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { + if (mSessionsListeners.get(i).mListener == listener) { + return i; + } + } + return -1; + } + private boolean isSessionDiscoverable(MediaSessionRecord record) { // TODO probably want to check more than if it's active. return record.isActive(); } + private void pushSessionsChanged(int userId) { + synchronized (mLock) { + List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId); + int size = records.size(); + ArrayList<MediaSessionToken> tokens = new ArrayList<MediaSessionToken>(); + for (int i = 0; i < size; i++) { + tokens.add(new MediaSessionToken(records.get(i).getControllerBinder())); + } + for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { + SessionsListenerRecord record = mSessionsListeners.get(i); + if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) { + try { + record.mListener.onActiveSessionsChanged(tokens); + } catch (RemoteException e) { + Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing", + e); + mSessionsListeners.remove(i); + } + } + } + } + } + private MediaRouteProviderProxy.RoutesListener mRoutesCallback = new MediaRouteProviderProxy.RoutesListener() { @Override @@ -613,6 +659,23 @@ public class MediaSessionService extends SystemService implements Monitor { }; } + final class SessionsListenerRecord implements IBinder.DeathRecipient { + private final IActiveSessionsListener mListener; + private final int mUserId; + + public SessionsListenerRecord(IActiveSessionsListener listener, int userId) { + mListener = listener; + mUserId = userId; + } + + @Override + public void binderDied() { + synchronized (mLock) { + mSessionsListeners.remove(this); + } + } + } + class SessionManagerImpl extends ISessionManager.Stub { private static final String EXTRA_WAKELOCK_ACQUIRED = "android.media.AudioService.WAKELOCK_ACQUIRED"; @@ -648,20 +711,7 @@ public class MediaSessionService extends SystemService implements Monitor { final long token = Binder.clearCallingIdentity(); try { - String packageName = null; - if (componentName != null) { - // If they gave us a component name verify they own the - // package - packageName = componentName.getPackageName(); - enforcePackageName(packageName, uid); - } - // Check that they can make calls on behalf of the user and - // get the final user id - int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, - true /* allowAll */, true /* requireFull */, "getSessions", packageName); - // Check if they have the permissions or their component is - // enabled for the user they're calling from. - enforceMediaPermissions(componentName, pid, uid, resolvedUserId); + int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); ArrayList<IBinder> binders = new ArrayList<IBinder>(); synchronized (mLock) { ArrayList<MediaSessionRecord> records = mPriorityStack @@ -677,6 +727,52 @@ public class MediaSessionService extends SystemService implements Monitor { } } + @Override + public void addSessionsListener(IActiveSessionsListener listener, + ComponentName componentName, int userId) throws RemoteException { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + try { + int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); + synchronized (mLock) { + int index = findIndexOfSessionsListenerLocked(listener); + if (index != -1) { + Log.w(TAG, "ActiveSessionsListener is already added, ignoring"); + return; + } + SessionsListenerRecord record = new SessionsListenerRecord(listener, + resolvedUserId); + try { + listener.asBinder().linkToDeath(record, 0); + } catch (RemoteException e) { + Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e); + return; + } + mSessionsListeners.add(record); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void removeSessionsListener(IActiveSessionsListener listener) + throws RemoteException { + synchronized (mLock) { + int index = findIndexOfSessionsListenerLocked(listener); + if (index != -1) { + SessionsListenerRecord record = mSessionsListeners.remove(index); + try { + record.mListener.asBinder().unlinkToDeath(record, 0); + } catch (Exception e) { + // ignore exceptions, the record is being removed + } + } + } + } + /** * Handles the dispatching of the media button events to one of the * registered listeners, or if there was none, broadcast an @@ -764,6 +860,25 @@ public class MediaSessionService extends SystemService implements Monitor { } } + private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, + final int uid) { + String packageName = null; + if (componentName != null) { + // If they gave us a component name verify they own the + // package + packageName = componentName.getPackageName(); + enforcePackageName(packageName, uid); + } + // Check that they can make calls on behalf of the user and + // get the final user id + int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, + true /* allowAll */, true /* requireFull */, "getSessions", packageName); + // Check if they have the permissions or their component is + // enabled for the user they're calling from. + enforceMediaPermissions(componentName, pid, uid, resolvedUserId); + return resolvedUserId; + } + private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags, MediaSessionRecord session) { int direction = 0; @@ -781,25 +896,36 @@ public class MediaSessionService extends SystemService implements Monitor { } if (session == null) { - for (int i = 0; i < steps; i++) { - try { - mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, - flags, getContext().getOpPackageName()); - } catch (RemoteException e) { - Log.e(TAG, "Error adjusting default volume.", e); + try { + if (delta == 0) { + mAudioService.adjustSuggestedStreamVolume(delta, suggestedStream, flags, + getContext().getOpPackageName()); + } else { + for (int i = 0; i < steps; i++) { + mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, + flags, getContext().getOpPackageName()); + } } + } catch (RemoteException e) { + Log.e(TAG, "Error adjusting default volume.", e); } } else { if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) { - for (int i = 0; i < steps; i++) { - try { - mAudioService.adjustSuggestedStreamVolume(direction, + try { + if (delta == 0) { + mAudioService.adjustSuggestedStreamVolume(delta, session.getAudioStream(), flags, getContext().getOpPackageName()); - } catch (RemoteException e) { - Log.e(TAG, "Error adjusting volume for stream " - + session.getAudioStream(), e); + } else { + for (int i = 0; i < steps; i++) { + mAudioService.adjustSuggestedStreamVolume(direction, + session.getAudioStream(), flags, + getContext().getOpPackageName()); + } } + } catch (RemoteException e) { + Log.e(TAG, "Error adjusting volume for stream " + + session.getAudioStream(), e); } } else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) { session.adjustVolumeBy(delta); @@ -994,4 +1120,20 @@ public class MediaSessionService extends SystemService implements Monitor { }; } + final class MessageHandler extends Handler { + private static final int MSG_SESSIONS_CHANGED = 1; + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SESSIONS_CHANGED: + pushSessionsChanged(msg.arg1); + break; + } + } + + public void post(int what, int arg1, int arg2) { + obtainMessage(what, arg1, arg2).sendToTarget(); + } + } } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 803dee296fb2..144ccfafc018 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -88,16 +88,19 @@ public class MediaSessionStack { * @param record The record that changed. * @param oldState Its old playback state. * @param newState Its new playback state. + * @return true if the priority order was updated, false otherwise. */ - public void onPlaystateChange(MediaSessionRecord record, int oldState, int newState) { + public boolean onPlaystateChange(MediaSessionRecord record, int oldState, int newState) { if (shouldUpdatePriority(oldState, newState)) { mSessions.remove(record); mSessions.add(0, record); clearCache(); + return true; } else if (newState == PlaybackState.STATE_PAUSED) { // Just clear the volume cache in this case mCachedVolumeDefault = null; } + return false; } /** diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java index a5f865fd95f5..27af0ed7c919 100644 --- a/services/core/java/com/android/server/task/TaskManagerService.java +++ b/services/core/java/com/android/server/task/TaskManagerService.java @@ -374,7 +374,7 @@ public class TaskManagerService extends com.android.server.SystemService public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule) { if (!stopTrackingTask(taskStatus)) { if (DEBUG) { - Slog.e(TAG, "Error removing task: could not find task to remove. Was task" + + Slog.e(TAG, "Error removing task: could not find task to remove. Was task " + "removed while executing?"); } return; diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java index 75e92127475d..686ca38e064f 100644 --- a/services/core/java/com/android/server/task/TaskServiceContext.java +++ b/services/core/java/com/android/server/task/TaskServiceContext.java @@ -466,16 +466,14 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon removeMessages(MSG_TIMEOUT); mWakeLock.release(); mContext.unbindService(TaskServiceContext.this); - mWakeLock = null; + mCompletedListener.onTaskCompleted(mRunningTask, reschedule); + mWakeLock = null; mRunningTask = null; mParams = null; mVerb = -1; mCancelled.set(false); - service = null; - - mCompletedListener.onTaskCompleted(mRunningTask, reschedule); synchronized (mAvailableLock) { mAvailable = true; } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 1c277a8dd961..10a67c4e421d 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -301,9 +301,8 @@ public final class TvInputManagerService extends SystemService { Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent( userState.inputMap.get(inputId).getComponent()); - mContext.bindServiceAsUser(i, serviceState.mConnection, Context.BIND_AUTO_CREATE, - new UserHandle(userId)); - serviceState.mBound = true; + serviceState.mBound = mContext.bindServiceAsUser( + i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId)); } else if (serviceState.mService != null && isStateEmpty) { // This means that the service is already connected but its state indicates that we have // nothing to do with it. Then, disconnect the service. diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index 3cfb45b593ee..db44d3a2893e 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -12,7 +12,6 @@ LOCAL_SRC_FILES += \ $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \ $(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \ $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \ - $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecService.cpp \ $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiMhlController.cpp \ $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \ $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \ diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp deleted file mode 100644 index 1d111a1e7990..000000000000 --- a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp +++ /dev/null @@ -1,756 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "HdmiCecJni" - -#define LOG_NDEBUG 1 - -#include "ScopedPrimitiveArray.h" - -#include <string> -#include <deque> -#include <map> - -#include <android_runtime/AndroidRuntime.h> -#include <android_runtime/Log.h> -#include <hardware/hdmi_cec.h> - -namespace android { - -static struct { - jmethodID handleMessage; - jmethodID handleHotplug; - jmethodID getActiveSource; - jmethodID getLanguage; -} gHdmiCecServiceClassInfo; - -#ifndef min -#define min(a, b) ((a) > (b) ? (b) : (a)) -#endif - -class HdmiCecHandler { -public: - enum HdmiCecError { - SUCCESS = 0, - FAILED = -1 - }; - - // Data type to hold a CEC message or internal event data. - typedef union { - cec_message_t cec; - hotplug_event_t hotplug; - } queue_item_t; - - // Entry used for message queue. - typedef std::pair<int, const queue_item_t> MessageEntry; - - HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj); - - void initialize(); - - // initialize individual logical device. - cec_logical_address_t initLogicalDevice(cec_device_type_t type); - void releaseLogicalDevice(cec_device_type_t type); - - cec_logical_address_t getLogicalAddress(cec_device_type_t deviceType); - uint16_t getPhysicalAddress(); - cec_device_type_t getDeviceType(cec_logical_address_t addr); - void queueMessage(const MessageEntry& message); - void queueOutgoingMessage(const cec_message_t& message); - void sendReportPhysicalAddress(cec_logical_address_t srcAddr); - void sendActiveSource(cec_logical_address_t srcAddr); - void sendFeatureAbort(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr, - int opcode, int reason); - void sendCecVersion(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr, - int version); - void sendDeviceVendorId(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr); - void sendGiveDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr); - void sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr, - const char* name, size_t len); - void sendSetMenuLanguage(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr); - - void sendCecMessage(const cec_message_t& message); - void setOsdName(const char* name, size_t len); - -private: - enum { - EVENT_TYPE_RX, - EVENT_TYPE_TX, - EVENT_TYPE_HOTPLUG, - EVENT_TYPE_STANDBY - }; - - /* - * logical address pool for each device type. - */ - static const cec_logical_address_t TV_ADDR_POOL[]; - static const cec_logical_address_t PLAYBACK_ADDR_POOL[]; - static const cec_logical_address_t RECORDER_ADDR_POOL[]; - static const cec_logical_address_t TUNER_ADDR_POOL[]; - - static const unsigned int MAX_BUFFER_SIZE = 256; - static const uint16_t INVALID_PHYSICAL_ADDRESS = 0xFFFF; - - static void onReceived(const hdmi_event_t* event, void* arg); - static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); - - void updatePhysicalAddress(); - void updateLogicalAddress(); - - // Allocate logical address. The CEC standard recommends that we try to use the address - // we have ever used before, in case this is to allocate an address afte the cable is - // connected again. If preferredAddr is given a valid one (not CEC_ADDR_UNREGISTERED), then - // this method checks if the address is available first. If not, it tries other addresses - // int the address pool available for the given type. - cec_logical_address_t allocateLogicalAddress(cec_device_type_t type, - cec_logical_address_t preferredAddr); - - // Send a CEC ping message. Returns true if successful. - bool sendPing(cec_logical_address_t addr); - - // Return the pool of logical addresses that are used for a given device type. - // One of the addresses in the pool will be chosen in the allocation logic. - bool getLogicalAddressPool(cec_device_type_t type, const cec_logical_address_t** addrPool, - size_t* poolSize); - - // Handles the message retrieved from internal message queue. The message can be - // for either rx or tx. - void dispatchMessage(const MessageEntry& message); - void processIncomingMessage(const cec_message_t& msg); - - // Check the message before we pass it up to framework. If true, we proceed. - // otherwise do not propagate it. - bool precheckMessage(const cec_message_t& msg); - - // Propagate the message up to Java layer. - void propagateMessage(const cec_message_t& msg); - void propagateHotplug(bool connected); - - // Handles incoming <Request Active Source> message. If one of logical - // devices is active, it should reply with <Active Source> message. - void handleRequestActiveSource(); - void handleGiveOsdName(const cec_message_t& msg); - void handleGiveDeviceVendorID(const cec_message_t& msg); - void handleGetCECVersion(const cec_message_t& msg); - void handleGetMenuLanguage(const cec_message_t& msg); - - // Internal thread for message queue handler - class HdmiThread : public Thread { - public: - HdmiThread(HdmiCecHandler* hdmiCecHandler, bool canCallJava) : - Thread(canCallJava), - mHdmiCecHandler(hdmiCecHandler) { - } - private: - virtual bool threadLoop() { - ALOGV("HdmiThread started"); - AutoMutex _l(mHdmiCecHandler->mMessageQueueLock); - mHdmiCecHandler->mMessageQueueCondition.wait(mHdmiCecHandler->mMessageQueueLock); - /* Process all messages in the queue */ - while (mHdmiCecHandler->mMessageQueue.size() > 0) { - MessageEntry entry = mHdmiCecHandler->mMessageQueue.front(); - mHdmiCecHandler->dispatchMessage(entry); - } - return true; - } - - HdmiCecHandler* mHdmiCecHandler; - }; - - // device type -> logical address mapping - std::map<cec_device_type_t, cec_logical_address_t> mLogicalDevices; - - hdmi_cec_device_t* mDevice; - jobject mCallbacksObj; - Mutex mLock; - Mutex mMessageQueueLock; - Condition mMessageQueueCondition; - sp<HdmiThread> mMessageQueueHandler; - - std::deque<MessageEntry> mMessageQueue; - uint16_t mPhysicalAddress; - std::string mOsdName; -}; - - const cec_logical_address_t HdmiCecHandler::TV_ADDR_POOL[] = { - CEC_ADDR_TV, - CEC_ADDR_FREE_USE, - }; - - const cec_logical_address_t HdmiCecHandler::PLAYBACK_ADDR_POOL[] = { - CEC_ADDR_PLAYBACK_1, - CEC_ADDR_PLAYBACK_2, - CEC_ADDR_PLAYBACK_3 - }; - - const cec_logical_address_t HdmiCecHandler::RECORDER_ADDR_POOL[] = { - CEC_ADDR_RECORDER_1, - CEC_ADDR_RECORDER_2, - CEC_ADDR_RECORDER_3 - }; - - const cec_logical_address_t HdmiCecHandler::TUNER_ADDR_POOL[] = { - CEC_ADDR_TUNER_1, - CEC_ADDR_TUNER_2, - CEC_ADDR_TUNER_3, - CEC_ADDR_TUNER_4 - }; - -HdmiCecHandler::HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj) : - mDevice(device), - mCallbacksObj(callbacksObj) { -} - -void HdmiCecHandler::initialize() { - mDevice->register_event_callback(mDevice, HdmiCecHandler::onReceived, this); - mMessageQueueHandler = new HdmiThread(this, true /* canCallJava */); - mMessageQueueHandler->run("MessageHandler"); - updatePhysicalAddress(); -} - -uint16_t HdmiCecHandler::getPhysicalAddress() { - return mPhysicalAddress; -} - -cec_logical_address_t HdmiCecHandler::initLogicalDevice(cec_device_type_t type) { - cec_logical_address addr = allocateLogicalAddress(type, CEC_ADDR_UNREGISTERED); - if (addr != CEC_ADDR_UNREGISTERED && !mDevice->add_logical_address(mDevice, addr)) { - mLogicalDevices.insert(std::pair<cec_device_type_t, cec_logical_address_t>(type, addr)); - - // Broadcast <Report Physical Address> when a new logical address was allocated to let - // other devices discover the new logical device and its logical - physical address - // association. - sendReportPhysicalAddress(addr); - } - return addr; -} - -void HdmiCecHandler::releaseLogicalDevice(cec_device_type_t type) { - std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.find(type); - if (it != mLogicalDevices.end()) { - mLogicalDevices.erase(it); - } - // TODO: remove the address monitored in HAL as well. -} - -cec_logical_address_t HdmiCecHandler::getLogicalAddress(cec_device_type_t type) { - std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.find(type); - if (it != mLogicalDevices.end()) { - return it->second; - } - return CEC_ADDR_UNREGISTERED; -} - -cec_device_type_t HdmiCecHandler::getDeviceType(cec_logical_address_t addr) { - std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin(); - for (; it != mLogicalDevices.end(); ++it) { - if (it->second == addr) { - return it->first; - } - } - return CEC_DEVICE_INACTIVE; -} - -void HdmiCecHandler::queueMessage(const MessageEntry& entry) { - AutoMutex _l(mMessageQueueLock); - if (mMessageQueue.size() <= MAX_BUFFER_SIZE) { - mMessageQueue.push_back(entry); - mMessageQueueCondition.signal(); - } else { - ALOGW("Queue is full! Message dropped."); - } -} - -void HdmiCecHandler::queueOutgoingMessage(const cec_message_t& message) { - queue_item_t item; - item.cec = message; - MessageEntry entry = std::make_pair(EVENT_TYPE_TX, item); - queueMessage(entry); -} - -void HdmiCecHandler::sendReportPhysicalAddress(cec_logical_address_t addr) { - if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { - ALOGE("Invalid physical address."); - return; - } - cec_device_type_t deviceType = getDeviceType(addr); - if (deviceType == CEC_DEVICE_INACTIVE) { - ALOGE("Invalid logical address: %d", addr); - return; - } - - cec_message_t msg; - msg.initiator = addr; - msg.destination = CEC_ADDR_BROADCAST; - msg.length = 4; - msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS; - msg.body[1] = (mPhysicalAddress >> 8) & 0xff; - msg.body[2] = mPhysicalAddress & 0xff; - msg.body[3] = deviceType; - queueOutgoingMessage(msg); -} - -void HdmiCecHandler::sendActiveSource(cec_logical_address_t srcAddr) { - if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) { - ALOGE("Error getting physical address."); - return; - } - cec_message_t msg; - msg.initiator = srcAddr; - msg.destination = CEC_ADDR_BROADCAST; - msg.length = 3; - msg.body[0] = CEC_MESSAGE_ACTIVE_SOURCE; - msg.body[1] = (mPhysicalAddress >> 8) & 0xff; - msg.body[2] = mPhysicalAddress & 0xff; - queueOutgoingMessage(msg); -} - -void HdmiCecHandler::sendFeatureAbort(cec_logical_address_t srcAddr, - cec_logical_address_t dstAddr, int opcode, int reason) { - cec_message_t msg; - msg.initiator = srcAddr; - msg.destination = dstAddr; - msg.length = 3; - msg.body[0] = CEC_MESSAGE_FEATURE_ABORT; - msg.body[1] = opcode; - msg.body[2] = reason; - queueOutgoingMessage(msg); -} - -void HdmiCecHandler::sendCecVersion(cec_logical_address_t srcAddr, - cec_logical_address_t dstAddr, int version) { - cec_message_t msg; - msg.initiator = srcAddr; - msg.destination = dstAddr; - msg.length = 2; - msg.body[0] = CEC_MESSAGE_CEC_VERSION; - msg.body[1] = version; - queueOutgoingMessage(msg); -} - -void HdmiCecHandler::sendGiveDeviceVendorID(cec_logical_address_t srcAddr, - cec_logical_address_t dstAddr) { - cec_message_t msg; - msg.initiator = srcAddr; - msg.destination = dstAddr; - msg.length = 1; - msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID; - queueOutgoingMessage(msg); -} - -void HdmiCecHandler::sendDeviceVendorId(cec_logical_address_t srcAddr, - cec_logical_address_t dstAddr) { - cec_message_t msg; - msg.initiator = srcAddr; - msg.destination = dstAddr; - msg.length = 4; - msg.body[0] = CEC_MESSAGE_DEVICE_VENDOR_ID; - uint32_t vendor_id; - mDevice->get_vendor_id(mDevice, &vendor_id); - msg.body[1] = (vendor_id >> 16) & 0xff; - msg.body[2] = (vendor_id >> 8) & 0xff; - msg.body[3] = vendor_id & 0xff; - queueOutgoingMessage(msg); -} - -void HdmiCecHandler::sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr, - const char* name, size_t len) { - cec_message_t msg; - msg.initiator = srcAddr; - msg.destination = dstAddr; - msg.body[0] = CEC_MESSAGE_SET_OSD_NAME; - msg.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH); - std::memcpy(msg.body + 1, name, msg.length - 1); - queueOutgoingMessage(msg); -} - -void HdmiCecHandler::sendSetMenuLanguage(cec_logical_address_t srcAddr, - cec_logical_address_t dstAddr) { - char lang[4]; // buffer for 3-letter language code - JNIEnv* env = AndroidRuntime::getJNIEnv(); - jstring res = (jstring) env->CallObjectMethod(mCallbacksObj, - gHdmiCecServiceClassInfo.getLanguage, - getDeviceType(srcAddr)); - const char *clang = env->GetStringUTFChars(res, NULL); - strlcpy(lang, clang, sizeof(lang)); - env->ReleaseStringUTFChars(res, clang); - - cec_message_t msg; - msg.initiator = srcAddr; - msg.destination = dstAddr; - msg.length = 4; // opcode (1) + language code (3) - msg.body[0] = CEC_MESSAGE_SET_MENU_LANGUAGE; - std::memcpy(msg.body + 1, lang, 3); - queueOutgoingMessage(msg); - checkAndClearExceptionFromCallback(env, __FUNCTION__); -} - -void HdmiCecHandler::sendCecMessage(const cec_message_t& message) { - AutoMutex _l(mLock); - ALOGV("sendCecMessage"); - mDevice->send_message(mDevice, &message); -} - -void HdmiCecHandler::setOsdName(const char* name, size_t len) { - mOsdName.assign(name, min(len, CEC_MESSAGE_BODY_MAX_LENGTH - 1)); -} - -// static -void HdmiCecHandler::onReceived(const hdmi_event_t* event, void* arg) { - HdmiCecHandler* handler = static_cast<HdmiCecHandler*>(arg); - if (handler == NULL) { - return; - } - queue_item_t item; - if (event->type == HDMI_EVENT_CEC_MESSAGE) { - item.cec = event->cec; - MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_RX, item); - handler->queueMessage(entry); - } else if (event->type == HDMI_EVENT_HOT_PLUG) { - item.hotplug = event->hotplug; - MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_HOTPLUG, item); - handler->queueMessage(entry); - } -} - -// static -void HdmiCecHandler::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { - if (env->ExceptionCheck()) { - ALOGE("An exception was thrown by callback '%s'.", methodName); - LOGE_EX(env); - env->ExceptionClear(); - } -} - -void HdmiCecHandler::updatePhysicalAddress() { - uint16_t addr; - if (!mDevice->get_physical_address(mDevice, &addr)) { - mPhysicalAddress = addr; - } else { - mPhysicalAddress = INVALID_PHYSICAL_ADDRESS; - } -} - -void HdmiCecHandler::updateLogicalAddress() { - mDevice->clear_logical_address(mDevice); - std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin(); - for (; it != mLogicalDevices.end(); ++it) { - cec_logical_address_t addr; - cec_logical_address_t preferredAddr = it->second; - cec_device_type_t deviceType = it->first; - addr = allocateLogicalAddress(deviceType, preferredAddr); - if (!mDevice->add_logical_address(mDevice, addr)) { - it->second = addr; - } else { - it->second = CEC_ADDR_UNREGISTERED; - } - } -} - -cec_logical_address_t HdmiCecHandler::allocateLogicalAddress(cec_device_type_t type, - cec_logical_address_t preferredAddr) { - const cec_logical_address_t* addrPool; - size_t poolSize; - if (getLogicalAddressPool(type, &addrPool, &poolSize) < 0) { - return CEC_ADDR_UNREGISTERED; - } - unsigned start = 0; - - // Find the index of preferred address in the pool. If not found, the start - // position will be 0. This happens when the passed preferredAddr is set to - // CEC_ADDR_UNREGISTERED, meaning that no preferred address is given. - for (unsigned i = 0; i < poolSize; i++) { - if (addrPool[i] == preferredAddr) { - start = i; - break; - } - } - for (unsigned i = 0; i < poolSize; i++) { - cec_logical_address_t addr = addrPool[(start + i) % poolSize]; - if (!sendPing(addr)) { - // Failure in pinging means the address is available, not taken by any device. - ALOGV("Logical Address Allocation success: %d", addr); - return addr; - } - } - ALOGE("Logical Address Allocation failed"); - return CEC_ADDR_UNREGISTERED; -} - -bool HdmiCecHandler::sendPing(cec_logical_address addr) { - cec_message_t msg; - msg.initiator = msg.destination = addr; - msg.length = 0; - return !mDevice->send_message(mDevice, &msg); - -} - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -bool HdmiCecHandler::getLogicalAddressPool(cec_device_type_t deviceType, - const cec_logical_address_t** addrPool, size_t* poolSize) { - switch (deviceType) { - case CEC_DEVICE_TV: - *addrPool = TV_ADDR_POOL; - *poolSize = ARRAY_SIZE(TV_ADDR_POOL); - break; - case CEC_DEVICE_RECORDER: - *addrPool = RECORDER_ADDR_POOL; - *poolSize = ARRAY_SIZE(RECORDER_ADDR_POOL); - break; - case CEC_DEVICE_TUNER: - *addrPool = TUNER_ADDR_POOL; - *poolSize = ARRAY_SIZE(TUNER_ADDR_POOL); - break; - case CEC_DEVICE_PLAYBACK: - *addrPool = PLAYBACK_ADDR_POOL; - *poolSize = ARRAY_SIZE(PLAYBACK_ADDR_POOL); - break; - default: - ALOGE("Unsupported device type: %d", deviceType); - return false; - } - return true; -} - -#undef ARRAY_SIZE - -void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) { - int type = entry.first; - mMessageQueueLock.unlock(); - if (type == EVENT_TYPE_RX) { - mMessageQueue.pop_front(); - processIncomingMessage(entry.second.cec); - } else if (type == EVENT_TYPE_TX) { - sendCecMessage(entry.second.cec); - mMessageQueue.pop_front(); - } else if (type == EVENT_TYPE_HOTPLUG) { - mMessageQueue.pop_front(); - bool connected = entry.second.hotplug.connected; - if (connected) { - updatePhysicalAddress(); - updateLogicalAddress(); - } - propagateHotplug(connected); - } - mMessageQueueLock.lock(); -} - -void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) { - int opcode = msg.body[0]; - if (opcode == CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS) { - sendReportPhysicalAddress(msg.destination); - } else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) { - handleRequestActiveSource(); - } else if (opcode == CEC_MESSAGE_GIVE_OSD_NAME) { - handleGiveOsdName(msg); - } else if (opcode == CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID) { - handleGiveDeviceVendorID(msg); - } else if (opcode == CEC_MESSAGE_GET_CEC_VERSION) { - handleGetCECVersion(msg); - } else if (opcode == CEC_MESSAGE_GET_MENU_LANGUAGE) { - handleGetMenuLanguage(msg); - } else if (opcode == CEC_MESSAGE_ABORT) { - // Compliance testing requires that abort message be responded with feature abort. - sendFeatureAbort(msg.destination, msg.initiator, msg.body[0], ABORT_REFUSED); - } else { - if (precheckMessage(msg)) { - propagateMessage(msg); - } - } -} - -bool HdmiCecHandler::precheckMessage(const cec_message_t& msg) { - // Check if this is the broadcast message coming to itself, which need not be passed - // back to framework. This happens because CEC spec specifies that a physical device - // may host multiple logical devices. A broadcast message sent by one of them therefore - // should be able to reach the others by the loopback mechanism. - // - // Currently we don't deal with multiple logical devices, so this is not necessary. - // It should be revisited once we support hosting multiple logical devices. - int opcode = msg.body[0]; - if (msg.destination == CEC_ADDR_BROADCAST && - (opcode == CEC_MESSAGE_ACTIVE_SOURCE || - opcode == CEC_MESSAGE_SET_STREAM_PATH || - opcode == CEC_MESSAGE_INACTIVE_SOURCE)) { - uint16_t senderAddr = (msg.body[1] << 8) + msg.body[2]; - if (senderAddr == mPhysicalAddress) { - return false; - } - } - return true; -} - -void HdmiCecHandler::propagateMessage(const cec_message_t& msg) { - int paramLen = msg.length - 1; - jint srcAddr = msg.initiator; - jint dstAddr = msg.destination; - jint opcode = msg.body[0]; - JNIEnv* env = AndroidRuntime::getJNIEnv(); - jbyteArray params = env->NewByteArray(paramLen); - const jbyte* body = reinterpret_cast<const jbyte *>(msg.body + 1); - if (paramLen > 0) { - env->SetByteArrayRegion(params, 0, paramLen, body); - } - env->CallVoidMethod(mCallbacksObj, - gHdmiCecServiceClassInfo.handleMessage, - srcAddr, dstAddr, opcode, params); - env->DeleteLocalRef(params); - checkAndClearExceptionFromCallback(env, __FUNCTION__); -} - -void HdmiCecHandler::propagateHotplug(bool connected) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod(mCallbacksObj, - gHdmiCecServiceClassInfo.handleHotplug, - connected); - checkAndClearExceptionFromCallback(env, __FUNCTION__); -} - - -void HdmiCecHandler::handleRequestActiveSource() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); - jint activeDeviceType = env->CallIntMethod(mCallbacksObj, - gHdmiCecServiceClassInfo.getActiveSource); - if (activeDeviceType != CEC_DEVICE_INACTIVE) { - sendActiveSource(getLogicalAddress(static_cast<cec_device_type_t>(activeDeviceType))); - } - checkAndClearExceptionFromCallback(env, __FUNCTION__); -} - -void HdmiCecHandler::handleGiveOsdName(const cec_message_t& msg) { - if (!mOsdName.empty()) { - sendSetOsdName(msg.destination, msg.initiator, mOsdName.c_str(), mOsdName.length()); - } -} - -void HdmiCecHandler::handleGiveDeviceVendorID(const cec_message_t& msg) { - sendDeviceVendorId(msg.destination, msg.initiator); -} - -void HdmiCecHandler::handleGetCECVersion(const cec_message_t& msg) { - int version; - mDevice->get_version(mDevice, &version); - sendCecVersion(msg.destination, msg.initiator, version); -} - -void HdmiCecHandler::handleGetMenuLanguage(const cec_message_t& msg) { - sendSetMenuLanguage(msg.destination, msg.initiator); -} - -//------------------------------------------------------------------------------ - -#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ - var = env->GetMethodID(clazz, methodName, methodDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find method " methodName); - -static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) { - int err; - hw_module_t* module; - err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, const_cast<const hw_module_t **>(&module)); - if (err != 0) { - ALOGE("Error acquiring hardware module: %d", err); - return 0; - } - hw_device_t* device; - err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device); - if (err != 0) { - ALOGE("Error opening hardware module: %d", err); - return 0; - } - HdmiCecHandler *handler = new HdmiCecHandler(reinterpret_cast<hdmi_cec_device *>(device), - env->NewGlobalRef(callbacksObj)); - handler->initialize(); - - GET_METHOD_ID(gHdmiCecServiceClassInfo.handleMessage, clazz, - "handleMessage", "(III[B)V"); - GET_METHOD_ID(gHdmiCecServiceClassInfo.handleHotplug, clazz, - "handleHotplug", "(Z)V"); - GET_METHOD_ID(gHdmiCecServiceClassInfo.getActiveSource, clazz, - "getActiveSource", "()I"); - GET_METHOD_ID(gHdmiCecServiceClassInfo.getLanguage, clazz, - "getLanguage", "(I)Ljava/lang/String;"); - - return reinterpret_cast<jlong>(handler); -} - -static void nativeSendMessage(JNIEnv* env, jclass clazz, jlong handlerPtr, jint deviceType, - jint dstAddr, jint opcode, jbyteArray params) { - HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); - cec_logical_address_t srcAddr = handler->getLogicalAddress( - static_cast<cec_device_type_t>(deviceType)); - jsize len = env->GetArrayLength(params); - ScopedByteArrayRO paramsPtr(env, params); - cec_message_t message; - message.initiator = srcAddr; - message.destination = static_cast<cec_logical_address_t>(dstAddr); - message.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH); - message.body[0] = opcode; - std::memcpy(message.body + 1, paramsPtr.get(), message.length - 1); - handler->sendCecMessage(message); -} - -static jint nativeAllocateLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr, - jint deviceType) { - HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); - return handler->initLogicalDevice(static_cast<cec_device_type_t>(deviceType)); -} - -static void nativeRemoveLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr, - jint deviceType) { - HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); - return handler->releaseLogicalDevice(static_cast<cec_device_type_t>(deviceType)); -} - -static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr) { - HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); - return handler->getPhysicalAddress(); -} - -static void nativeSetOsdName(JNIEnv* env, jclass clazz, jlong handlerPtr, jbyteArray name) { - HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr); - jsize len = env->GetArrayLength(name); - if (len > 0) { - ScopedByteArrayRO namePtr(env, name); - handler->setOsdName(reinterpret_cast<const char *>(namePtr.get()), len); - } -} - -static JNINativeMethod sMethods[] = { - /* name, signature, funcPtr */ - { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecService;)J", - (void *)nativeInit }, - { "nativeSendMessage", "(JIII[B)V", - (void *)nativeSendMessage }, - { "nativeAllocateLogicalAddress", "(JI)I", - (void *)nativeAllocateLogicalAddress }, - { "nativeRemoveLogicalAddress", "(JI)V", - (void *)nativeRemoveLogicalAddress }, - { "nativeGetPhysicalAddress", "(J)I", - (void *)nativeGetPhysicalAddress }, - { "nativeSetOsdName", "(J[B)V", - (void *)nativeSetOsdName }, -}; - -#define CLASS_PATH "com/android/server/hdmi/HdmiCecService" - -int register_android_server_hdmi_HdmiCecService(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods)); - LOG_FATAL_IF(res < 0, "Unable to register native methods."); - return 0; -} - -} /* namespace android */ diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index bfa8286a1458..a3021044b78c 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -39,7 +39,6 @@ int register_android_server_location_FlpHardwareProvider(JNIEnv* env); int register_android_server_connectivity_Vpn(JNIEnv* env); int register_android_server_dreams_McuHal(JNIEnv* env); int register_android_server_hdmi_HdmiCecController(JNIEnv* env); -int register_android_server_hdmi_HdmiCecService(JNIEnv* env); int register_android_server_hdmi_HdmiMhlController(JNIEnv* env); int register_android_server_tv_TvInputHal(JNIEnv* env); }; @@ -76,8 +75,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_dreams_McuHal(env); register_android_server_BatteryStatsService(env); register_android_server_hdmi_HdmiCecController(env); - // TODO: remove this once replaces HdmiCecService with HdmiControlService. - register_android_server_hdmi_HdmiCecService(env); register_android_server_hdmi_HdmiMhlController(env); register_android_server_tv_TvInputHal(env); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e8b7b69fbf78..2aa1220929e9 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -129,8 +129,6 @@ public final class SystemServer { "com.android.server.wifi.passpoint.PasspointService"; private static final String WIFI_P2P_SERVICE_CLASS = "com.android.server.wifi.p2p.WifiP2pService"; - private static final String HDMI_CEC_SERVICE_CLASS = - "com.android.server.hdmi.HdmiCecService"; private static final String ETHERNET_SERVICE_CLASS = "com.android.server.ethernet.EthernetService"; private static final String TASK_SERVICE_CLASS = @@ -954,12 +952,6 @@ public final class SystemServer { } try { - mSystemServiceManager.startService(HDMI_CEC_SERVICE_CLASS); - } catch (Throwable e) { - reportWtf("starting HdmiCec Service", e); - } - - try { mSystemServiceManager.startService(HdmiControlService.class); } catch (Throwable e) { reportWtf("starting HdmiControlService", e); diff --git a/tests/JobSchedulerTestApp/Android.mk b/tests/JobSchedulerTestApp/Android.mk new file mode 100644 index 000000000000..7336d8c6a00d --- /dev/null +++ b/tests/JobSchedulerTestApp/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := JobSchedulerTestApp + +LOCAL_PROGUARD_ENABLED := disabled + +include $(BUILD_PACKAGE) + diff --git a/tests/JobSchedulerTestApp/AndroidManifest.xml b/tests/JobSchedulerTestApp/AndroidManifest.xml new file mode 100644 index 000000000000..74317377d31f --- /dev/null +++ b/tests/JobSchedulerTestApp/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.demo.jobSchedulerApp" > + + <uses-sdk + android:minSdkVersion="18" + android:targetSdkVersion="18" /> + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name="com.android.demo.jobSchedulerApp.MainActivity" + android:label="@string/app_name" + android:windowSoftInputMode="stateHidden" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <service + android:name=".service.TestJobService" + android:exported="true"/> + </application> + +</manifest> diff --git a/tests/JobSchedulerTestApp/res/drawable-hdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..a0f7005a31c1 --- /dev/null +++ b/tests/JobSchedulerTestApp/res/drawable-hdpi/ic_launcher.png diff --git a/tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..a085462c2542 --- /dev/null +++ b/tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.png diff --git a/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.png b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000000..4f5d2558fb4a --- /dev/null +++ b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.png diff --git a/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..4f78eb846f0c --- /dev/null +++ b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.png diff --git a/tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..b198ee3e9fff --- /dev/null +++ b/tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.png diff --git a/tests/JobSchedulerTestApp/res/layout/activity_main.xml b/tests/JobSchedulerTestApp/res/layout/activity_main.xml new file mode 100644 index 000000000000..7f4961b94040 --- /dev/null +++ b/tests/JobSchedulerTestApp/res/layout/activity_main.xml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" > + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="100dp"> + <TextView + android:id="@+id/onstart_textview" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_weight="1" + android:background="@color/none_received" + android:gravity="center" + android:text="@string/onstarttask"/> + <TextView + android:id="@+id/onstop_textview" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_weight="1" + android:background="@color/none_received" + android:gravity="center" + android:text="@string/onstoptask"/> + </LinearLayout> + <Button + android:id="@+id/finished_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="20dp" + android:layout_marginBottom="5dp" + android:onClick="finishJob" + android:text="@string/finish_job_button_text"/> + + <TextView + android:id="@+id/task_params" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/defaultparamtext" + android:gravity="center" + android:textSize="20dp" + + android:padding="15dp" + android:layout_marginBottom="10dp" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/constraints" + android:textSize="18dp"/> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginLeft="10dp"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/connectivity" + android:layout_marginRight="10dp"/> + <RadioGroup + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <RadioButton android:id="@+id/checkbox_any" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/any"/> + <RadioButton android:id="@+id/checkbox_unmetered" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/unmetered"/> + </RadioGroup> + + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/timing"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="15dp" + android:textSize="17dp" + android:text="@string/delay"/> + <EditText + android:id="@+id/delay_time" + android:layout_width="60dp" + android:layout_height="wrap_content" + android:inputType="number"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/deadline" + android:textSize="17dp"/> + <EditText + android:id="@+id/deadline_time" + android:layout_width="60dp" + android:layout_height="wrap_content" + android:inputType="number"/> + </LinearLayout> + + </LinearLayout> + <Button + android:id="@+id/schedule_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="40dp" + android:onClick="scheduleJob" + android:text="@string/schedule_job_button_text"/> + </LinearLayout> +</LinearLayout> diff --git a/tests/JobSchedulerTestApp/res/values-v11/styles.xml b/tests/JobSchedulerTestApp/res/values-v11/styles.xml new file mode 100644 index 000000000000..ff6530178183 --- /dev/null +++ b/tests/JobSchedulerTestApp/res/values-v11/styles.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 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. +--> + +<resources> + + <!-- + Base application theme for API 11+. This theme completely replaces + AppBaseTheme from res/values/styles.xml on API 11+ devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light"> + <!-- API 11 theme customizations can go here. --> + </style> + +</resources>
\ No newline at end of file diff --git a/tests/JobSchedulerTestApp/res/values-v14/styles.xml b/tests/JobSchedulerTestApp/res/values-v14/styles.xml new file mode 100644 index 000000000000..a4a443afe085 --- /dev/null +++ b/tests/JobSchedulerTestApp/res/values-v14/styles.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 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. +--> + +<resources> + + <!-- + Base application theme for API 14+. This theme completely replaces + AppBaseTheme from BOTH res/values/styles.xml and + res/values-v11/styles.xml on API 14+ devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar"> + <!-- API 14 theme customizations can go here. --> + </style> + +</resources>
\ No newline at end of file diff --git a/tests/JobSchedulerTestApp/res/values/color.xml b/tests/JobSchedulerTestApp/res/values/color.xml new file mode 100644 index 000000000000..7bd3a91b269f --- /dev/null +++ b/tests/JobSchedulerTestApp/res/values/color.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2014 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<resources> + <color name="none_received">#999999</color> + <color name="start_received">#00FF00</color> + <color name="stop_received">#FF0000</color> +</resources>
\ No newline at end of file diff --git a/tests/JobSchedulerTestApp/res/values/strings.xml b/tests/JobSchedulerTestApp/res/values/strings.xml new file mode 100644 index 000000000000..824d4b198446 --- /dev/null +++ b/tests/JobSchedulerTestApp/res/values/strings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 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. +--> + +<resources> + <string name="onstoptask">onStopTask</string> + <string name="onstarttask">onStartTask</string> + <string name="defaultparamtext">task params will show up here.</string> + <string name="schedule_job_button_text">Schedule Job</string> + <string name="app_name">Job Scheduler Test</string> + <string name="finish_job_button_text">taskFinished</string> + <string name="manual_sync_text">Manual Sync</string> + <string name="constraints">Constraints</string> + <string name="connectivity">Connectivity:</string> + <string name="any">Any</string> + <string name="unmetered">WiFi</string> + <string name="timing">Timing:</string> + <string name="delay">Delay:</string> + <string name="deadline">Deadline:</string> +</resources> diff --git a/tests/JobSchedulerTestApp/res/values/styles.xml b/tests/JobSchedulerTestApp/res/values/styles.xml new file mode 100644 index 000000000000..43a8f2b0885e --- /dev/null +++ b/tests/JobSchedulerTestApp/res/values/styles.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 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. +--> + +<resources> + + <!-- + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + --> + <style name="AppBaseTheme" parent="android:Theme.Light"> + <!-- + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + --> + </style> + + <!-- Application theme. --> + <style name="AppTheme" parent="AppBaseTheme"> + <!-- All customizations that are NOT specific to a particular API-level can go here. --> + </style> + +</resources>
\ No newline at end of file diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java new file mode 100644 index 000000000000..393c59444b52 --- /dev/null +++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java @@ -0,0 +1,169 @@ +/* + * Copyright 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.demo.jobSchedulerApp; + +import android.app.Activity; +import android.app.task.Task; +import android.app.task.TaskParams; +import android.content.ComponentName; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.text.TextUtils; +import android.view.View; +import android.widget.EditText; +import android.widget.RadioButton; +import android.widget.TextView; +import android.widget.Toast; + +import com.android.demo.jobSchedulerApp.service.TestJobService; + +public class MainActivity extends Activity { + + private static final String TAG = "MainActivity"; + + public static final int MSG_UNCOLOUR_START = 0; + public static final int MSG_UNCOLOUR_STOP = 1; + public static final int MSG_SERVICE_OBJ = 2; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + Resources res = getResources(); + defaultColor = res.getColor(R.color.none_received); + startJobColor = res.getColor(R.color.start_received); + stopJobColor = res.getColor(R.color.stop_received); + + // Set up UI. + mShowStartView = (TextView) findViewById(R.id.onstart_textview); + mShowStopView = (TextView) findViewById(R.id.onstop_textview); + mParamsTextView = (TextView) findViewById(R.id.task_params); + mDelayEditText = (EditText) findViewById(R.id.delay_time); + mDeadlineEditText = (EditText) findViewById(R.id.deadline_time); + mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered); + mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any); + + mServiceComponent = new ComponentName(this, TestJobService.class); + // Start service and provide it a way to communicate with us. + Intent startServiceIntent = new Intent(this, TestJobService.class); + startServiceIntent.putExtra("messenger", new Messenger(mHandler)); + startService(startServiceIntent); + } + // UI fields. + int defaultColor; + int startJobColor; + int stopJobColor; + + TextView mShowStartView; + TextView mShowStopView; + TextView mParamsTextView; + EditText mDelayEditText; + EditText mDeadlineEditText; + RadioButton mWiFiConnectivityRadioButton; + RadioButton mAnyConnectivityRadioButton; + ComponentName mServiceComponent; + /** Service object to interact scheduled tasks. */ + TestJobService mTestService; + + private static int kTaskId = 0; + + Handler mHandler = new Handler(/* default looper */) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UNCOLOUR_START: + mShowStartView.setBackgroundColor(defaultColor); + break; + case MSG_UNCOLOUR_STOP: + mShowStopView.setBackgroundColor(defaultColor); + break; + case MSG_SERVICE_OBJ: + mTestService = (TestJobService) msg.obj; + mTestService.setUiCallback(MainActivity.this); + } + } + }; + + private boolean ensureTestService() { + if (mTestService == null) { + Toast.makeText(MainActivity.this, "Service null, never got callback?", + Toast.LENGTH_SHORT).show(); + return false; + } + return true; + } + + /** + * UI onclick listener to schedule a task. What this task is is defined in + * TestJobService#scheduleJob() + */ + public void scheduleJob(View v) { + if (!ensureTestService()) { + return; + } + + Task.Builder builder = new Task.Builder(kTaskId++, mServiceComponent); + + String delay = mDelayEditText.getText().toString(); + if (delay != null && !TextUtils.isEmpty(delay)) { + builder.setMinimumLatency(Long.valueOf(delay)); + } + String deadline = mDeadlineEditText.getText().toString(); + if (deadline != null && !TextUtils.isEmpty(deadline)) { + builder.setOverrideDeadline(Long.valueOf(deadline)); + } + boolean requiresUnmetered = mWiFiConnectivityRadioButton.isSelected(); + boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isSelected(); + if (requiresUnmetered) { + builder.setRequiredNetworkCapabilities(Task.NetworkType.UNMETERED); + } else if (requiresAnyConnectivity) { + builder.setRequiredNetworkCapabilities(Task.NetworkType.ANY); + } + + mTestService.scheduleJob(builder.build()); + + } + + /** + * UI onclick listener to call taskFinished() in our service. + */ + public void finishJob(View v) { + if (!ensureTestService()) { + return; + } + mTestService.callTaskFinished(); + mParamsTextView.setText(""); + } + + public void onReceivedStartTask(TaskParams params) { + mShowStartView.setBackgroundColor(startJobColor); + Message m = Message.obtain(mHandler, MSG_UNCOLOUR_START); + mHandler.sendMessageDelayed(m, 1000L); // uncolour in 1 second. + mParamsTextView.setText("Executing: " + params.getTaskId() + " " + params.getExtras()); + } + + public void onReceivedStopTask() { + mShowStopView.setBackgroundColor(stopJobColor); + Message m = Message.obtain(mHandler, MSG_UNCOLOUR_STOP); + mHandler.sendMessageDelayed(m, 2000L); // uncolour in 1 second. + mParamsTextView.setText(""); + } +} diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java new file mode 100644 index 000000000000..7dd3cf1c4b6f --- /dev/null +++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java @@ -0,0 +1,127 @@ +/* + * Copyright 2013 Google Inc. + * + * 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.demo.jobSchedulerApp.service; + +import android.app.Service; +import android.app.task.Task; +import android.app.task.TaskManager; +import android.app.task.TaskParams; +import android.app.task.TaskService; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.PersistableBundle; +import android.os.RemoteException; +import android.util.Log; + +import com.android.demo.jobSchedulerApp.MainActivity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + + +/** + * Service to handle sync requests. + * <p> + * This service is invoked in response to Intents with action android.content.SyncAdapter, and + * returns a Binder connection to SyncAdapter. + * <p> + * For performance, only one sync adapter will be initialized within this application's context. + * <p> + * Note: The SyncService itself is not notified when a new sync occurs. It's role is to manage the + * lifecycle of our and provide a handle to said SyncAdapter to the OS on + * request. + */ +public class TestJobService extends TaskService { + private static final String TAG = "SyncService"; + + @Override + public void onCreate() { + super.onCreate(); + Log.i(TAG, "Service created"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.i(TAG, "Service destroyed"); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Messenger callback = intent.getParcelableExtra("messenger"); + Message m = Message.obtain(); + m.what = MainActivity.MSG_SERVICE_OBJ; + m.obj = this; + try { + callback.send(m); + } catch (RemoteException e) { + Log.e(TAG, "Error passing service object back to activity."); + } + return START_NOT_STICKY; + } + + @Override + public boolean onStartTask(TaskParams params) { + taskParamsMap.add(params); + if (mActivity != null) { + mActivity.onReceivedStartTask(params); + } + Log.i(TAG, "on start task: " + params.getTaskId()); + return true; + } + + @Override + public boolean onStopTask(TaskParams params) { + taskParamsMap.remove(params); + mActivity.onReceivedStopTask(); + Log.i(TAG, "on stop task: " + params.getTaskId()); + return true; + } + + MainActivity mActivity; + private final LinkedList<TaskParams> taskParamsMap = new LinkedList<TaskParams>(); + + public void setUiCallback(MainActivity activity) { + mActivity = activity; + } + + /** Send job to the JobScheduler. */ + public void scheduleJob(Task t) { + Log.d(TAG, "Scheduling job"); + TaskManager tm = + (TaskManager) getSystemService(Context.TASK_SERVICE); + tm.schedule(t); + } + + public boolean callTaskFinished() { + TaskParams params = taskParamsMap.poll(); + if (params == null) { + return false; + } else { + taskFinished(params, false); + return true; + } + } + +} |