summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk19
-rw-r--r--api/current.txt36
-rw-r--r--core/java/android/app/ContextImpl.java8
-rw-r--r--core/java/android/app/Notification.java11
-rw-r--r--core/java/android/content/Context.java13
-rw-r--r--core/java/android/content/Intent.java8
-rw-r--r--core/java/android/content/pm/PackageManager.java2
-rw-r--r--core/java/android/hardware/hdmi/HdmiCecClient.java119
-rw-r--r--core/java/android/hardware/hdmi/HdmiCecManager.java68
-rw-r--r--core/java/android/hardware/hdmi/IHdmiCecService.aidl40
-rw-r--r--core/res/res/values/attrs.xml14
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--media/java/android/media/MediaRouter.java161
-rw-r--r--media/java/android/media/session/IActiveSessionsListener.aidl (renamed from core/java/android/hardware/hdmi/IHdmiCecListener.aidl)17
-rw-r--r--media/java/android/media/session/ISessionController.aidl2
-rw-r--r--media/java/android/media/session/ISessionManager.aidl4
-rw-r--r--media/java/android/media/session/MediaController.java15
-rw-r--r--media/java/android/media/session/MediaSession.java44
-rw-r--r--media/java/android/media/session/MediaSessionInfo.aidl18
-rw-r--r--media/java/android/media/session/MediaSessionInfo.java12
-rw-r--r--media/java/android/media/session/MediaSessionManager.java75
-rw-r--r--media/java/android/media/session/MediaSessionToken.java2
-rw-r--r--media/java/android/media/session/RemoteVolumeProvider.java15
-rw-r--r--media/java/android/media/tv/TvContract.java2
-rw-r--r--media/java/android/media/tv/TvInputInfo.java34
-rw-r--r--media/java/android/media/tv/TvView.java36
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java2
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml48
-rw-r--r--packages/SystemUI/res/layout/status_bar_ticker.xml62
-rw-r--r--packages/SystemUI/res/values/config.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecDevice.java230
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java129
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java35
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecService.java383
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java8
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java198
-rw-r--r--services/core/java/com/android/server/media/MediaSessionStack.java5
-rw-r--r--services/core/java/com/android/server/task/TaskManagerService.java2
-rw-r--r--services/core/java/com/android/server/task/TaskServiceContext.java6
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java5
-rw-r--r--services/core/jni/Android.mk1
-rw-r--r--services/core/jni/com_android_server_hdmi_HdmiCecService.cpp756
-rw-r--r--services/core/jni/onload.cpp3
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--tests/JobSchedulerTestApp/Android.mk15
-rw-r--r--tests/JobSchedulerTestApp/AndroidManifest.xml31
-rw-r--r--tests/JobSchedulerTestApp/res/drawable-hdpi/ic_launcher.pngbin0 -> 5473 bytes
-rw-r--r--tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.pngbin0 -> 3298 bytes
-rw-r--r--tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.pngbin0 -> 856 bytes
-rw-r--r--tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.pngbin0 -> 7401 bytes
-rw-r--r--tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 12074 bytes
-rw-r--r--tests/JobSchedulerTestApp/res/layout/activity_main.xml125
-rw-r--r--tests/JobSchedulerTestApp/res/values-v11/styles.xml28
-rw-r--r--tests/JobSchedulerTestApp/res/values-v14/styles.xml29
-rw-r--r--tests/JobSchedulerTestApp/res/values/color.xml21
-rw-r--r--tests/JobSchedulerTestApp/res/values/strings.xml33
-rw-r--r--tests/JobSchedulerTestApp/res/values/styles.xml37
-rw-r--r--tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java169
-rw-r--r--tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java127
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 &lt;Active Source&gt; 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 &lt;Inactive Source&gt; 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 &lt;Image View On&gt; 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 &lt;Text View On&gt; 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 &lt;Request Active
- * Source &gt;.
- *
- * @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
- * &lt;Set Menu Language&gt; 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
new file mode 100644
index 000000000000..a0f7005a31c1
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000000..a085462c2542
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.png b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.png
new file mode 100644
index 000000000000..4f5d2558fb4a
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.png
Binary files differ
diff --git a/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000000..4f78eb846f0c
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000000..b198ee3e9fff
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
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;
+ }
+ }
+
+}