• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.input;
18 
19 import android.annotation.BinderThread;
20 import android.hardware.input.IStickyModifierStateListener;
21 import android.os.IBinder;
22 import android.os.RemoteException;
23 import android.util.Log;
24 import android.util.Slog;
25 import android.util.SparseArray;
26 
27 import com.android.internal.annotations.GuardedBy;
28 
29 /**
30  * A thread-safe component of {@link InputManagerService} responsible for managing the sticky
31  * modifier state for A11y Sticky keys feature.
32  */
33 final class StickyModifierStateController {
34 
35     private static final String TAG = "ModifierStateController";
36 
37     // To enable these logs, run:
38     // 'adb shell setprop log.tag.ModifierStateController DEBUG' (requires restart)
39     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
40 
41     // List of currently registered sticky modifier state listeners
42     @GuardedBy("mStickyModifierStateListenerRecords")
43     private final SparseArray<StickyModifierStateListenerRecord>
44             mStickyModifierStateListenerRecords = new SparseArray<>();
45 
notifyStickyModifierStateChanged(int modifierState, int lockedModifierState)46     public void notifyStickyModifierStateChanged(int modifierState, int lockedModifierState) {
47         if (DEBUG) {
48             Slog.d(TAG, "Sticky modifier state changed, modifierState = " + modifierState
49                     + ", lockedModifierState = " + lockedModifierState);
50         }
51 
52         synchronized (mStickyModifierStateListenerRecords) {
53             for (int i = 0; i < mStickyModifierStateListenerRecords.size(); i++) {
54                 mStickyModifierStateListenerRecords.valueAt(i).notifyStickyModifierStateChanged(
55                         modifierState, lockedModifierState);
56             }
57         }
58     }
59 
60     /** Register the sticky modifier state listener for a process. */
61     @BinderThread
registerStickyModifierStateListener(IStickyModifierStateListener listener, int pid)62     public void registerStickyModifierStateListener(IStickyModifierStateListener listener,
63             int pid) {
64         synchronized (mStickyModifierStateListenerRecords) {
65             if (mStickyModifierStateListenerRecords.get(pid) != null) {
66                 throw new IllegalStateException("The calling process has already registered "
67                         + "a StickyModifierStateListener.");
68             }
69             StickyModifierStateListenerRecord record = new StickyModifierStateListenerRecord(pid,
70                     listener);
71             try {
72                 listener.asBinder().linkToDeath(record, 0);
73             } catch (RemoteException ex) {
74                 throw new RuntimeException(ex);
75             }
76             mStickyModifierStateListenerRecords.put(pid, record);
77         }
78     }
79 
80     /** Unregister the sticky modifier state listener for a process. */
81     @BinderThread
unregisterStickyModifierStateListener(IStickyModifierStateListener listener, int pid)82     public void unregisterStickyModifierStateListener(IStickyModifierStateListener listener,
83             int pid) {
84         synchronized (mStickyModifierStateListenerRecords) {
85             StickyModifierStateListenerRecord record = mStickyModifierStateListenerRecords.get(pid);
86             if (record == null) {
87                 throw new IllegalStateException("The calling process has no registered "
88                         + "StickyModifierStateListener.");
89             }
90             if (record.mListener.asBinder() != listener.asBinder()) {
91                 throw new IllegalStateException("The calling process has a different registered "
92                         + "StickyModifierStateListener.");
93             }
94             record.mListener.asBinder().unlinkToDeath(record, 0);
95             mStickyModifierStateListenerRecords.remove(pid);
96         }
97     }
98 
onStickyModifierStateListenerDied(int pid)99     private void onStickyModifierStateListenerDied(int pid) {
100         synchronized (mStickyModifierStateListenerRecords) {
101             mStickyModifierStateListenerRecords.remove(pid);
102         }
103     }
104 
105     // A record of a registered sticky modifier state listener from one process.
106     private class StickyModifierStateListenerRecord implements IBinder.DeathRecipient {
107         public final int mPid;
108         public final IStickyModifierStateListener mListener;
109 
StickyModifierStateListenerRecord(int pid, IStickyModifierStateListener listener)110         StickyModifierStateListenerRecord(int pid, IStickyModifierStateListener listener) {
111             mPid = pid;
112             mListener = listener;
113         }
114 
115         @Override
binderDied()116         public void binderDied() {
117             if (DEBUG) {
118                 Slog.d(TAG, "Sticky modifier state listener for pid " + mPid + " died.");
119             }
120             onStickyModifierStateListenerDied(mPid);
121         }
122 
notifyStickyModifierStateChanged(int modifierState, int lockedModifierState)123         public void notifyStickyModifierStateChanged(int modifierState, int lockedModifierState) {
124             try {
125                 mListener.onStickyModifierStateChanged(modifierState, lockedModifierState);
126             } catch (RemoteException ex) {
127                 Slog.w(TAG, "Failed to notify process " + mPid
128                         + " that sticky modifier state changed, assuming it died.", ex);
129                 binderDied();
130             }
131         }
132     }
133 }
134