* commit '59c1a0e1cb6cad46e30821e25f99c1ecce1227f0': Add TabletModeChangedListener for SystemUI.
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user