diff options
author | Michael Wright <michaelwr@google.com> | 2015-09-03 14:00:52 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-09-03 14:00:52 +0000 |
commit | 8ae90587a510f72a176119b1f23add8bc36285b9 (patch) | |
tree | 1f919ca6a19605612ef6a8f40337ba28506ae218 | |
parent | bd5b61c489fb0543c8db29030e57189c273010eb (diff) | |
parent | 39e5e947447bc611205404ae6a4690656f1aa0f9 (diff) | |
download | frameworks_base-8ae90587a510f72a176119b1f23add8bc36285b9.zip frameworks_base-8ae90587a510f72a176119b1f23add8bc36285b9.tar.gz frameworks_base-8ae90587a510f72a176119b1f23add8bc36285b9.tar.bz2 |
Merge "Add TabletModeChangedListener for SystemUI." into mnc-dr-dev
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | core/java/android/hardware/input/IInputManager.aidl | 4 | ||||
-rw-r--r-- | core/java/android/hardware/input/ITabletModeChangedListener.aidl | 23 | ||||
-rw-r--r-- | core/java/android/hardware/input/InputManager.java | 141 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 6 | ||||
-rw-r--r-- | services/core/java/com/android/server/input/InputManagerService.java | 111 |
6 files changed, 286 insertions, 0 deletions
@@ -173,6 +173,7 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \ core/java/android/hardware/input/IInputManager.aidl \ core/java/android/hardware/input/IInputDevicesChangedListener.aidl \ + core/java/android/hardware/input/ITabletModeChangedListener.aidl \ core/java/android/hardware/location/IActivityRecognitionHardware.aidl \ core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl \ core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl \ diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 465d142..c8b45c7 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -19,6 +19,7 @@ package android.hardware.input; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.KeyboardLayout; import android.hardware.input.IInputDevicesChangedListener; +import android.hardware.input.ITabletModeChangedListener; import android.hardware.input.TouchCalibration; import android.os.IBinder; import android.view.InputDevice; @@ -60,6 +61,9 @@ interface IInputManager { // Registers an input devices changed listener. void registerInputDevicesChangedListener(IInputDevicesChangedListener listener); + // Registers a tablet mode change listener + void registerTabletModeChangedListener(ITabletModeChangedListener listener); + // Input device vibrator control. void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token); void cancelVibrate(int deviceId, IBinder token); diff --git a/core/java/android/hardware/input/ITabletModeChangedListener.aidl b/core/java/android/hardware/input/ITabletModeChangedListener.aidl new file mode 100644 index 0000000..a8559a7 --- /dev/null +++ b/core/java/android/hardware/input/ITabletModeChangedListener.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input; + +/** @hide */ +interface ITabletModeChangedListener { + /* Called when the device enters or exits tablet mode. */ + oneway void onTabletModeChanged(long whenNanos, boolean inTabletMode); +} diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 444f020..a754d6b 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -16,6 +16,7 @@ package android.hardware.input; +import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import android.annotation.SdkConstant; @@ -29,6 +30,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.os.Vibrator; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -38,6 +40,7 @@ import android.view.InputDevice; import android.view.InputEvent; import java.util.ArrayList; +import java.util.List; /** * Provides information about input devices and available key layouts. @@ -67,6 +70,11 @@ public final class InputManager { private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners = new ArrayList<InputDeviceListenerDelegate>(); + // Guarded by mTabletModeLock + private final Object mTabletModeLock = new Object(); + private TabletModeChangedListener mTabletModeChangedListener; + private List<OnTabletModeChangedListenerDelegate> mOnTabletModeChangedListeners; + /** * Broadcast Action: Query available keyboard layouts. * <p> @@ -331,6 +339,72 @@ public final class InputManager { } /** + * Register a tablet mode changed listener. + * + * @param listener The listener to register. + * @param handler The handler on which the listener should be invoked, or null + * if the listener should be invoked on the calling thread's looper. + * @hide + */ + public void registerOnTabletModeChangedListener( + OnTabletModeChangedListener listener, Handler handler) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + synchronized (mTabletModeLock) { + if (mOnTabletModeChangedListeners == null) { + initializeTabletModeListenerLocked(); + } + int idx = findOnTabletModeChangedListenerLocked(listener); + if (idx < 0) { + OnTabletModeChangedListenerDelegate d = + new OnTabletModeChangedListenerDelegate(listener, handler); + mOnTabletModeChangedListeners.add(d); + } + } + } + + /** + * Unregister a tablet mode changed listener. + * + * @param listener The listener to unregister. + * @hide + */ + public void unregisterOnTabletModeChangedListener(OnTabletModeChangedListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + synchronized (mTabletModeLock) { + int idx = findOnTabletModeChangedListenerLocked(listener); + if (idx >= 0) { + OnTabletModeChangedListenerDelegate d = mOnTabletModeChangedListeners.remove(idx); + d.removeCallbacksAndMessages(null); + } + } + } + + private void initializeTabletModeListenerLocked() { + final TabletModeChangedListener listener = new TabletModeChangedListener(); + try { + mIm.registerTabletModeChangedListener(listener); + } catch (RemoteException ex) { + throw new RuntimeException("Could not register tablet mode changed listener", ex); + } + mTabletModeChangedListener = listener; + mOnTabletModeChangedListeners = new ArrayList<>(); + } + + private int findOnTabletModeChangedListenerLocked(OnTabletModeChangedListener listener) { + final int N = mOnTabletModeChangedListeners.size(); + for (int i = 0; i < N; i++) { + if (mOnTabletModeChangedListeners.get(i).mListener == listener) { + return i; + } + } + return -1; + } + + /** * Gets information about all supported keyboard layouts. * <p> * The input manager consults the built-in keyboard layouts as well @@ -769,6 +843,22 @@ public final class InputManager { return false; } + + private void onTabletModeChanged(long whenNanos, boolean inTabletMode) { + if (DEBUG) { + Log.d(TAG, "Received tablet mode changed: " + + "whenNanos=" + whenNanos + ", inTabletMode=" + inTabletMode); + } + synchronized (mTabletModeLock) { + final int N = mOnTabletModeChangedListeners.size(); + for (int i = 0; i < N; i++) { + OnTabletModeChangedListenerDelegate listener = + mOnTabletModeChangedListeners.get(i); + listener.sendTabletModeChanged(whenNanos, inTabletMode); + } + } + } + /** * Gets a vibrator service associated with an input device, assuming it has one. * @return The vibrator, never null. @@ -838,6 +928,57 @@ public final class InputManager { } } + /** @hide */ + public interface OnTabletModeChangedListener { + /** + * Called whenever the device goes into or comes out of tablet mode. + * + * @param whenNanos The time at which the device transitioned into or + * out of tablet mode. This is given in nanoseconds in the + * {@link SystemClock#uptimeMillis} time base. + */ + void onTabletModeChanged(long whenNanos, boolean inTabletMode); + } + + private final class TabletModeChangedListener extends ITabletModeChangedListener.Stub { + @Override + public void onTabletModeChanged(long whenNanos, boolean inTabletMode) { + InputManager.this.onTabletModeChanged(whenNanos, inTabletMode); + } + } + + private static final class OnTabletModeChangedListenerDelegate extends Handler { + private static final int MSG_TABLET_MODE_CHANGED = 0; + + public final OnTabletModeChangedListener mListener; + + public OnTabletModeChangedListenerDelegate( + OnTabletModeChangedListener listener, Handler handler) { + super(handler != null ? handler.getLooper() : Looper.myLooper()); + mListener = listener; + } + + public void sendTabletModeChanged(long whenNanos, boolean inTabletMode) { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = (int) (whenNanos & 0xFFFFFFFF); + args.argi2 = (int) (whenNanos >> 32); + args.arg1 = (Boolean) inTabletMode; + obtainMessage(MSG_TABLET_MODE_CHANGED, args).sendToTarget(); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_TABLET_MODE_CHANGED: + SomeArgs args = (SomeArgs) msg.obj; + long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32); + boolean inTabletMode = (boolean) args.arg1; + mListener.onTabletModeChanged(whenNanos, inTabletMode); + break; + } + } + } + private final class InputDeviceVibrator extends Vibrator { private final int mDeviceId; private final Binder mToken; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b60fdba..c8bb675 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2054,6 +2054,12 @@ <permission android:name="android.permission.SET_KEYBOARD_LAYOUT" android:protectionLevel="signature" /> + <!-- Allows an application to monitor changes in tablet mode. + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.TABLET_MODE_LISTENER" + android:protectionLevel="signature" /> + <!-- Allows an application to request installing packages. Apps targeting APIs greater than 22 must hold this permission in order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 17b4f9c..5a13672 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -17,6 +17,7 @@ package com.android.server.input; import android.view.Display; +import com.android.internal.os.SomeArgs; import com.android.internal.R; import com.android.internal.util.XmlUtils; import com.android.server.DisplayThread; @@ -52,6 +53,7 @@ import android.hardware.input.IInputManager; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputManager; import android.hardware.input.InputManagerInternal; +import android.hardware.input.ITabletModeChangedListener; import android.hardware.input.KeyboardLayout; import android.hardware.input.TouchCalibration; import android.os.Binder; @@ -93,6 +95,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import libcore.io.Streams; import libcore.util.Objects; @@ -112,6 +115,7 @@ public class InputManagerService extends IInputManager.Stub private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3; private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4; private static final int MSG_RELOAD_DEVICE_ALIASES = 5; + private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 6; // Pointer to native input manager service object. private final long mPtr; @@ -124,6 +128,13 @@ public class InputManagerService extends IInputManager.Stub private boolean mSystemReady; private NotificationManager mNotificationManager; + private final Object mTabletModeLock = new Object(); + // List of currently registered tablet mode changed listeners by process id + private final SparseArray<TabletModeChangedListenerRecord> mTabletModeChangedListeners = + new SparseArray<>(); // guarded by mTabletModeLock + private final List<TabletModeChangedListenerRecord> mTempTabletModeChangedListenersToNotify = + new ArrayList<>(); + // Persistent data store. Must be locked each time during use. private final PersistentDataStore mDataStore = new PersistentDataStore(); @@ -227,6 +238,11 @@ public class InputManagerService extends IInputManager.Stub /** Switch code: Lid switch. When set, lid is shut. */ public static final int SW_LID = 0x00; + /** Switch code: Tablet mode switch. + * When set, the device is in tablet mode (i.e. no keyboard is connected). + */ + public static final int SW_TABLET_MODE = 0x01; + /** Switch code: Keypad slide. When set, keyboard is exposed. */ public static final int SW_KEYPAD_SLIDE = 0x0a; @@ -246,6 +262,7 @@ public class InputManagerService extends IInputManager.Stub public static final int SW_CAMERA_LENS_COVER = 0x09; public static final int SW_LID_BIT = 1 << SW_LID; + public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE; public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE; public static final int SW_HEADPHONE_INSERT_BIT = 1 << SW_HEADPHONE_INSERT; public static final int SW_MICROPHONE_INSERT_BIT = 1 << SW_MICROPHONE_INSERT; @@ -774,6 +791,57 @@ public class InputManagerService extends IInputManager.Stub } } + @Override // Binder call + public void registerTabletModeChangedListener(ITabletModeChangedListener listener) { + if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE_LISTENER, + "registerTabletModeChangedListener()")) { + throw new SecurityException("Requires TABLET_MODE_LISTENER permission"); + } + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + + synchronized (mTabletModeLock) { + final int callingPid = Binder.getCallingPid(); + if (mTabletModeChangedListeners.get(callingPid) != null) { + throw new IllegalStateException("The calling process has already registered " + + "a TabletModeChangedListener."); + } + TabletModeChangedListenerRecord record = + new TabletModeChangedListenerRecord(callingPid, listener); + try { + IBinder binder = listener.asBinder(); + binder.linkToDeath(record, 0); + } catch (RemoteException ex) { + throw new RuntimeException(ex); + } + mTabletModeChangedListeners.put(callingPid, record); + } + } + + private void onTabletModeChangedListenerDied(int pid) { + synchronized (mTabletModeLock) { + mTabletModeChangedListeners.remove(pid); + } + } + + // Must be called on handler + private void deliverTabletModeChanged(long whenNanos, boolean inTabletMode) { + mTempTabletModeChangedListenersToNotify.clear(); + final int numListeners; + synchronized (mTabletModeLock) { + numListeners = mTabletModeChangedListeners.size(); + for (int i = 0; i < numListeners; i++) { + mTempTabletModeChangedListenersToNotify.add( + mTabletModeChangedListeners.valueAt(i)); + } + } + for (int i = 0; i < numListeners; i++) { + mTempTabletModeChangedListenersToNotify.get(i).notifyTabletModeChanged( + whenNanos, inTabletMode); + } + } + // Must be called on handler. private void showMissingKeyboardLayoutNotification(InputDevice device) { if (!mKeyboardLayoutNotificationShown) { @@ -1419,6 +1487,15 @@ public class InputManagerService extends IInputManager.Stub mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues, switchMask); } + + if ((switchMask & SW_TABLET_MODE) != 0) { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = (int) (whenNanos & 0xFFFFFFFF); + args.argi2 = (int) (whenNanos >> 32); + args.arg1 = Boolean.valueOf((switchValues & SW_TABLET_MODE_BIT) != 0); + mHandler.obtainMessage(MSG_DELIVER_TABLET_MODE_CHANGED, + args).sendToTarget(); + } } // Native callback. @@ -1664,6 +1741,12 @@ public class InputManagerService extends IInputManager.Stub case MSG_RELOAD_DEVICE_ALIASES: reloadDeviceAliases(); break; + case MSG_DELIVER_TABLET_MODE_CHANGED: + SomeArgs args = (SomeArgs) msg.obj; + long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32); + boolean inTabletMode = (boolean) args.arg1; + deliverTabletModeChanged(whenNanos, inTabletMode); + break; } } } @@ -1755,6 +1838,34 @@ public class InputManagerService extends IInputManager.Stub } } + private final class TabletModeChangedListenerRecord implements DeathRecipient { + private final int mPid; + private final ITabletModeChangedListener mListener; + + public TabletModeChangedListenerRecord(int pid, ITabletModeChangedListener listener) { + mPid = pid; + mListener = listener; + } + + @Override + public void binderDied() { + if (DEBUG) { + Slog.d(TAG, "Tablet mode changed listener for pid " + mPid + " died."); + } + onTabletModeChangedListenerDied(mPid); + } + + public void notifyTabletModeChanged(long whenNanos, boolean inTabletMode) { + try { + mListener.onTabletModeChanged(whenNanos, inTabletMode); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify process " + mPid + + " that tablet mode changed, assuming it died.", ex); + binderDied(); + } + } + } + private final class VibratorToken implements DeathRecipient { public final int mDeviceId; public final IBinder mToken; |