am 59c1a0e1: am 8ae90587: Merge "Add TabletModeChangedListener for SystemUI." into mnc-dr-dev

* commit '59c1a0e1cb6cad46e30821e25f99c1ecce1227f0':
  Add TabletModeChangedListener for SystemUI.
This commit is contained in:
Michael Wright
2015-09-03 14:20:44 +00:00
committed by Android Git Automerger
6 changed files with 286 additions and 0 deletions

View File

@@ -173,6 +173,7 @@ LOCAL_SRC_FILES += \
core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \ core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl \
core/java/android/hardware/input/IInputManager.aidl \ core/java/android/hardware/input/IInputManager.aidl \
core/java/android/hardware/input/IInputDevicesChangedListener.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/IActivityRecognitionHardware.aidl \
core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl \ core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl \
core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl \ core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl \

View File

@@ -19,6 +19,7 @@ package android.hardware.input;
import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.KeyboardLayout; import android.hardware.input.KeyboardLayout;
import android.hardware.input.IInputDevicesChangedListener; import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.TouchCalibration; import android.hardware.input.TouchCalibration;
import android.os.IBinder; import android.os.IBinder;
import android.view.InputDevice; import android.view.InputDevice;
@@ -60,6 +61,9 @@ interface IInputManager {
// Registers an input devices changed listener. // Registers an input devices changed listener.
void registerInputDevicesChangedListener(IInputDevicesChangedListener listener); void registerInputDevicesChangedListener(IInputDevicesChangedListener listener);
// Registers a tablet mode change listener
void registerTabletModeChangedListener(ITabletModeChangedListener listener);
// Input device vibrator control. // Input device vibrator control.
void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token); void vibrate(int deviceId, in long[] pattern, int repeat, IBinder token);
void cancelVibrate(int deviceId, IBinder token); void cancelVibrate(int deviceId, IBinder token);

View File

@@ -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);
}

View File

@@ -16,6 +16,7 @@
package android.hardware.input; package android.hardware.input;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import android.annotation.SdkConstant; import android.annotation.SdkConstant;
@@ -29,6 +30,7 @@ import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Vibrator; import android.os.Vibrator;
import android.provider.Settings; import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException; import android.provider.Settings.SettingNotFoundException;
@@ -38,6 +40,7 @@ import android.view.InputDevice;
import android.view.InputEvent; import android.view.InputEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
/** /**
* Provides information about input devices and available key layouts. * Provides information about input devices and available key layouts.
@@ -67,6 +70,11 @@ public final class InputManager {
private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners = private final ArrayList<InputDeviceListenerDelegate> mInputDeviceListeners =
new ArrayList<InputDeviceListenerDelegate>(); 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. * Broadcast Action: Query available keyboard layouts.
* <p> * <p>
@@ -330,6 +338,72 @@ public final class InputManager {
return -1; return -1;
} }
/**
* 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. * Gets information about all supported keyboard layouts.
* <p> * <p>
@@ -769,6 +843,22 @@ public final class InputManager {
return false; 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. * Gets a vibrator service associated with an input device, assuming it has one.
* @return The vibrator, never null. * @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 class InputDeviceVibrator extends Vibrator {
private final int mDeviceId; private final int mDeviceId;
private final Binder mToken; private final Binder mToken;

View File

@@ -2061,6 +2061,12 @@
<permission android:name="android.permission.SET_KEYBOARD_LAYOUT" <permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
android:protectionLevel="signature" /> 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 <!-- Allows an application to request installing packages. Apps
targeting APIs greater than 22 must hold this permission in targeting APIs greater than 22 must hold this permission in
order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}.

View File

@@ -17,6 +17,7 @@
package com.android.server.input; package com.android.server.input;
import android.view.Display; import android.view.Display;
import com.android.internal.os.SomeArgs;
import com.android.internal.R; import com.android.internal.R;
import com.android.internal.util.XmlUtils; import com.android.internal.util.XmlUtils;
import com.android.server.DisplayThread; import com.android.server.DisplayThread;
@@ -52,6 +53,7 @@ import android.hardware.input.IInputManager;
import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager; import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal; import android.hardware.input.InputManagerInternal;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.KeyboardLayout; import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration; import android.hardware.input.TouchCalibration;
import android.os.Binder; import android.os.Binder;
@@ -93,6 +95,7 @@ import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import libcore.io.Streams; import libcore.io.Streams;
import libcore.util.Objects; 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_RELOAD_KEYBOARD_LAYOUTS = 3;
private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4; private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4;
private static final int MSG_RELOAD_DEVICE_ALIASES = 5; 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. // Pointer to native input manager service object.
private final long mPtr; private final long mPtr;
@@ -124,6 +128,13 @@ public class InputManagerService extends IInputManager.Stub
private boolean mSystemReady; private boolean mSystemReady;
private NotificationManager mNotificationManager; 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. // Persistent data store. Must be locked each time during use.
private final PersistentDataStore mDataStore = new PersistentDataStore(); 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. */ /** Switch code: Lid switch. When set, lid is shut. */
public static final int SW_LID = 0x00; 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. */ /** Switch code: Keypad slide. When set, keyboard is exposed. */
public static final int SW_KEYPAD_SLIDE = 0x0a; 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_CAMERA_LENS_COVER = 0x09;
public static final int SW_LID_BIT = 1 << SW_LID; 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_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
public static final int SW_HEADPHONE_INSERT_BIT = 1 << SW_HEADPHONE_INSERT; public static final int SW_HEADPHONE_INSERT_BIT = 1 << SW_HEADPHONE_INSERT;
public static final int SW_MICROPHONE_INSERT_BIT = 1 << SW_MICROPHONE_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. // Must be called on handler.
private void showMissingKeyboardLayoutNotification(InputDevice device) { private void showMissingKeyboardLayoutNotification(InputDevice device) {
if (!mKeyboardLayoutNotificationShown) { if (!mKeyboardLayoutNotificationShown) {
@@ -1419,6 +1487,15 @@ public class InputManagerService extends IInputManager.Stub
mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues, mWiredAccessoryCallbacks.notifyWiredAccessoryChanged(whenNanos, switchValues,
switchMask); 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. // Native callback.
@@ -1664,6 +1741,12 @@ public class InputManagerService extends IInputManager.Stub
case MSG_RELOAD_DEVICE_ALIASES: case MSG_RELOAD_DEVICE_ALIASES:
reloadDeviceAliases(); reloadDeviceAliases();
break; 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 { private final class VibratorToken implements DeathRecipient {
public final int mDeviceId; public final int mDeviceId;
public final IBinder mToken; public final IBinder mToken;