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

* commit '8ae90587a510f72a176119b1f23add8bc36285b9':
  Add TabletModeChangedListener for SystemUI.
This commit is contained in:
Michael Wright
2015-09-03 14:10:05 +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/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 \

View File

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

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;
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>
@@ -330,6 +338,72 @@ public final class InputManager {
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.
* <p>
@@ -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;

View File

@@ -2061,6 +2061,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}.

View File

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