• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.devicestate;
18 
19 import static android.Manifest.permission.CONTROL_DEVICE_STATE;
20 import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
21 import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
22 import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES;
23 
24 import android.annotation.IntRange;
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.content.Context;
28 import android.hardware.devicestate.DeviceStateInfo;
29 import android.hardware.devicestate.DeviceStateManager;
30 import android.hardware.devicestate.IDeviceStateManager;
31 import android.hardware.devicestate.IDeviceStateManagerCallback;
32 import android.os.Binder;
33 import android.os.IBinder;
34 import android.os.RemoteException;
35 import android.os.ResultReceiver;
36 import android.os.ShellCallback;
37 import android.util.ArrayMap;
38 import android.util.ArraySet;
39 import android.util.Slog;
40 import android.util.SparseArray;
41 
42 import com.android.internal.annotations.GuardedBy;
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.util.DumpUtils;
45 import com.android.internal.util.FrameworkStatsLog;
46 import com.android.server.SystemService;
47 import com.android.server.policy.DeviceStatePolicyImpl;
48 
49 import java.io.FileDescriptor;
50 import java.io.PrintWriter;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Optional;
54 
55 /**
56  * A system service that manages the state of a device with user-configurable hardware like a
57  * foldable phone.
58  * <p>
59  * Device state is an abstract concept that allows mapping the current state of the device to the
60  * state of the system. For example, system services (like
61  * {@link com.android.server.display.DisplayManagerService display manager} and
62  * {@link com.android.server.wm.WindowManagerService window manager}) and system UI may have
63  * different behaviors depending on the physical state of the device. This is useful for
64  * variable-state devices, like foldable or rollable devices, that can be configured by users into
65  * differing hardware states, which each may have a different expected use case.
66  * </p>
67  * <p>
68  * The {@link DeviceStateManagerService} is responsible for receiving state change requests from
69  * the {@link DeviceStateProvider} to modify the current device state and communicating with the
70  * {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state.
71  * </p>
72  * The service also provides the {@link DeviceStateManager} API allowing clients to listen for
73  * changes in device state and submit requests to override the device state provided by the
74  * {@link DeviceStateProvider}.
75  *
76  * @see DeviceStatePolicy
77  * @see DeviceStateManager
78  */
79 public final class DeviceStateManagerService extends SystemService {
80     private static final String TAG = "DeviceStateManagerService";
81     private static final boolean DEBUG = false;
82 
83     private final Object mLock = new Object();
84     @NonNull
85     private final DeviceStatePolicy mDeviceStatePolicy;
86     @NonNull
87     private final BinderService mBinderService;
88 
89     // All supported device states keyed by identifier.
90     @GuardedBy("mLock")
91     private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
92 
93     // The current committed device state. Will be empty until the first device state provided by
94     // the DeviceStateProvider is committed.
95     @GuardedBy("mLock")
96     @NonNull
97     private Optional<DeviceState> mCommittedState = Optional.empty();
98     // The device state that is currently awaiting callback from the policy to be committed.
99     @GuardedBy("mLock")
100     @NonNull
101     private Optional<DeviceState> mPendingState = Optional.empty();
102     // Whether or not the policy is currently waiting to be notified of the current pending state.
103     @GuardedBy("mLock")
104     private boolean mIsPolicyWaitingForState = false;
105 
106     // The device state that is set by the DeviceStateProvider. Will be empty until the first
107     // callback from the provider and then will always contain the most recent value.
108     @GuardedBy("mLock")
109     @NonNull
110     private Optional<DeviceState> mBaseState = Optional.empty();
111 
112     // List of processes registered to receive notifications about changes to device state and
113     // request status indexed by process id.
114     @GuardedBy("mLock")
115     private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
116     // List of override requests with the highest precedence request at the end.
117     @GuardedBy("mLock")
118     private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>();
119     // Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified
120     // of a change in status.
121     @GuardedBy("mLock")
122     private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>();
123 
DeviceStateManagerService(@onNull Context context)124     public DeviceStateManagerService(@NonNull Context context) {
125         this(context, new DeviceStatePolicyImpl(context));
126     }
127 
128     @VisibleForTesting
DeviceStateManagerService(@onNull Context context, @NonNull DeviceStatePolicy policy)129     DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
130         super(context);
131         mDeviceStatePolicy = policy;
132         mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
133         mBinderService = new BinderService();
134     }
135 
136     @Override
onStart()137     public void onStart() {
138         publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
139     }
140 
141     /**
142      * Returns the current state the system is in. Note that the system may be in the process of
143      * configuring a different state.
144      * <p>
145      * Note: This method will return {@link Optional#empty()} if called before the first state has
146      * been committed, otherwise it will return the last committed state.
147      *
148      * @see #getPendingState()
149      */
150     @NonNull
getCommittedState()151     Optional<DeviceState> getCommittedState() {
152         synchronized (mLock) {
153             return mCommittedState;
154         }
155     }
156 
157     /**
158      * Returns the state the system is currently configuring, or {@link Optional#empty()} if the
159      * system is not in the process of configuring a state.
160      */
161     @VisibleForTesting
162     @NonNull
getPendingState()163     Optional<DeviceState> getPendingState() {
164         synchronized (mLock) {
165             return mPendingState;
166         }
167     }
168 
169     /**
170      * Returns the base state. The service will configure the device to match the base state when
171      * there is no active request to override the base state.
172      * <p>
173      * Note: This method will return {@link Optional#empty()} if called before a base state is
174      * provided to the service by the {@link DeviceStateProvider}, otherwise it will return the
175      * most recent provided value.
176      *
177      * @see #getOverrideState()
178      */
179     @NonNull
getBaseState()180     Optional<DeviceState> getBaseState() {
181         synchronized (mLock) {
182             return mBaseState;
183         }
184     }
185 
186     /**
187      * Returns the current override state, or {@link Optional#empty()} if no override state is
188      * requested. If an override states is present, the returned state will take precedence over
189      * the base state returned from {@link #getBaseState()}.
190      */
191     @NonNull
getOverrideState()192     Optional<DeviceState> getOverrideState() {
193         synchronized (mLock) {
194             if (mRequestRecords.isEmpty()) {
195                 return Optional.empty();
196             }
197 
198             OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1);
199             return Optional.of(topRequest.mRequestedState);
200         }
201     }
202 
203     /** Returns the list of currently supported device states. */
getSupportedStates()204     DeviceState[] getSupportedStates() {
205         synchronized (mLock) {
206             DeviceState[] supportedStates = new DeviceState[mDeviceStates.size()];
207             for (int i = 0; i < supportedStates.length; i++) {
208                 supportedStates[i] = mDeviceStates.valueAt(i);
209             }
210             return supportedStates;
211         }
212     }
213 
214     /** Returns the list of currently supported device state identifiers. */
getSupportedStateIdentifiers()215     private int[] getSupportedStateIdentifiers() {
216         synchronized (mLock) {
217             return getSupportedStateIdentifiersLocked();
218         }
219     }
220 
221     /** Returns the list of currently supported device state identifiers. */
getSupportedStateIdentifiersLocked()222     private int[] getSupportedStateIdentifiersLocked() {
223         int[] supportedStates = new int[mDeviceStates.size()];
224         for (int i = 0; i < supportedStates.length; i++) {
225             supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier();
226         }
227         return supportedStates;
228     }
229 
230     @NonNull
getDeviceStateInfoLocked()231     private DeviceStateInfo getDeviceStateInfoLocked() {
232         if (!mBaseState.isPresent() || !mCommittedState.isPresent()) {
233             throw new IllegalStateException("Trying to get the current DeviceStateInfo before the"
234                     + " initial state has been committed.");
235         }
236 
237         final int[] supportedStates = getSupportedStateIdentifiersLocked();
238         final int baseState = mBaseState.get().getIdentifier();
239         final int currentState = mCommittedState.get().getIdentifier();
240 
241         return new DeviceStateInfo(supportedStates, baseState, currentState);
242     }
243 
244     @VisibleForTesting
getBinderService()245     IDeviceStateManager getBinderService() {
246         return mBinderService;
247     }
248 
updateSupportedStates(DeviceState[] supportedDeviceStates)249     private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
250         boolean updatedPendingState;
251         boolean hasBaseState;
252         synchronized (mLock) {
253             final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();
254 
255             mDeviceStates.clear();
256             for (int i = 0; i < supportedDeviceStates.length; i++) {
257                 DeviceState state = supportedDeviceStates[i];
258                 mDeviceStates.put(state.getIdentifier(), state);
259             }
260 
261             final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked();
262             if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) {
263                 return;
264             }
265 
266             final int requestSize = mRequestRecords.size();
267             for (int i = 0; i < requestSize; i++) {
268                 OverrideRequestRecord request = mRequestRecords.get(i);
269                 if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) {
270                     request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
271                 }
272             }
273 
274             updatedPendingState = updatePendingStateLocked();
275             hasBaseState = mBaseState.isPresent();
276         }
277 
278         if (hasBaseState && !updatedPendingState) {
279             // If the change in the supported states didn't result in a change of the pending state
280             // commitPendingState() will never be called and the callbacks will never be notified
281             // of the change.
282             notifyDeviceStateInfoChanged();
283         }
284 
285         notifyRequestsOfStatusChangeIfNeeded();
286         notifyPolicyIfNeeded();
287     }
288 
289     /**
290      * Returns {@code true} if the provided state is supported. Requires that
291      * {@link #mDeviceStates} is sorted prior to calling.
292      */
isSupportedStateLocked(int identifier)293     private boolean isSupportedStateLocked(int identifier) {
294         return mDeviceStates.contains(identifier);
295     }
296 
297     /**
298      * Returns the {@link DeviceState} with the supplied {@code identifier}, or {@code null} if
299      * there is no device state with the identifier.
300      */
301     @Nullable
getStateLocked(int identifier)302     private Optional<DeviceState> getStateLocked(int identifier) {
303         return Optional.ofNullable(mDeviceStates.get(identifier));
304     }
305 
306     /**
307      * Sets the base state.
308      *
309      * @throws IllegalArgumentException if the {@code identifier} is not a supported state.
310      *
311      * @see #isSupportedStateLocked(int)
312      */
setBaseState(int identifier)313     private void setBaseState(int identifier) {
314         boolean updatedPendingState;
315         synchronized (mLock) {
316             final Optional<DeviceState> baseStateOptional = getStateLocked(identifier);
317             if (!baseStateOptional.isPresent()) {
318                 throw new IllegalArgumentException("Base state is not supported");
319             }
320 
321             final DeviceState baseState = baseStateOptional.get();
322             if (mBaseState.isPresent() && mBaseState.get().equals(baseState)) {
323                 // Base state hasn't changed. Nothing to do.
324                 return;
325             }
326             mBaseState = Optional.of(baseState);
327 
328             final int requestSize = mRequestRecords.size();
329             for (int i = 0; i < requestSize; i++) {
330                 OverrideRequestRecord request = mRequestRecords.get(i);
331                 if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) {
332                     request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
333                 }
334             }
335 
336             updatedPendingState = updatePendingStateLocked();
337         }
338 
339         if (!updatedPendingState) {
340             // If the change in base state didn't result in a change of the pending state
341             // commitPendingState() will never be called and the callbacks will never be notified
342             // of the change.
343             notifyDeviceStateInfoChanged();
344         }
345 
346         notifyRequestsOfStatusChangeIfNeeded();
347         notifyPolicyIfNeeded();
348     }
349 
350     /**
351      * Tries to update the current pending state with the current requested state. Must call
352      * {@link #notifyPolicyIfNeeded()} to actually notify the policy that the state is being
353      * changed.
354      *
355      * @return {@code true} if the pending state has changed as a result of this call, {@code false}
356      * otherwise.
357      */
updatePendingStateLocked()358     private boolean updatePendingStateLocked() {
359         if (mPendingState.isPresent()) {
360             // Have pending state, can not configure a new state until the state is committed.
361             return false;
362         }
363 
364         final DeviceState stateToConfigure;
365         if (!mRequestRecords.isEmpty()) {
366             stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
367         } else if (mBaseState.isPresent()
368                 && isSupportedStateLocked(mBaseState.get().getIdentifier())) {
369             // Base state could have recently become unsupported after a change in supported states.
370             stateToConfigure = mBaseState.get();
371         } else {
372             stateToConfigure = null;
373         }
374 
375         if (stateToConfigure == null) {
376             // No currently requested state.
377             return false;
378         }
379 
380         if (mCommittedState.isPresent() && stateToConfigure.equals(mCommittedState.get())) {
381             // The state requesting to be committed already matches the current committed state.
382             return false;
383         }
384 
385         mPendingState = Optional.of(stateToConfigure);
386         mIsPolicyWaitingForState = true;
387         return true;
388     }
389 
390     /**
391      * Notifies the policy to configure the supplied state. Should not be called with {@link #mLock}
392      * held.
393      */
notifyPolicyIfNeeded()394     private void notifyPolicyIfNeeded() {
395         if (Thread.holdsLock(mLock)) {
396             Throwable error = new Throwable("Attempting to notify DeviceStatePolicy with service"
397                     + " lock held");
398             error.fillInStackTrace();
399             Slog.w(TAG, error);
400         }
401         int state;
402         synchronized (mLock) {
403             if (!mIsPolicyWaitingForState) {
404                 return;
405             }
406             mIsPolicyWaitingForState = false;
407             state = mPendingState.get().getIdentifier();
408         }
409 
410         if (DEBUG) {
411             Slog.d(TAG, "Notifying policy to configure state: " + state);
412         }
413         mDeviceStatePolicy.configureDeviceForState(state, this::commitPendingState);
414     }
415 
416     /**
417      * Commits the current pending state after a callback from the {@link DeviceStatePolicy}.
418      *
419      * <pre>
420      *              -------------    -----------              -------------
421      * Provider ->  | Requested | -> | Pending | -> Policy -> | Committed |
422      *              -------------    -----------              -------------
423      * </pre>
424      * <p>
425      * When a new state is requested it immediately enters the requested state. Once the policy is
426      * available to accept a new state, which could also be immediately if there is no current
427      * pending state at the point of request, the policy is notified and a callback is provided to
428      * trigger the state to be committed.
429      * </p>
430      */
commitPendingState()431     private void commitPendingState() {
432         // Update the current state.
433         synchronized (mLock) {
434             final DeviceState newState = mPendingState.get();
435             if (DEBUG) {
436                 Slog.d(TAG, "Committing state: " + newState);
437             }
438 
439             if (!mRequestRecords.isEmpty()) {
440                 final OverrideRequestRecord topRequest =
441                         mRequestRecords.get(mRequestRecords.size() - 1);
442                 if (topRequest.mRequestedState.getIdentifier() == newState.getIdentifier()) {
443                     // The top request could have come in while the service was awaiting callback
444                     // from the policy. In that case we only set it to active if it matches the
445                     // current committed state, otherwise it will be set to active when its
446                     // requested state is committed.
447                     topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
448                 }
449             }
450 
451             FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED,
452                     newState.getIdentifier(), !mCommittedState.isPresent());
453 
454             mCommittedState = Optional.of(newState);
455             mPendingState = Optional.empty();
456             updatePendingStateLocked();
457         }
458 
459         // Notify callbacks of a change.
460         notifyDeviceStateInfoChanged();
461 
462         // Notify the top request that it's active.
463         notifyRequestsOfStatusChangeIfNeeded();
464 
465         // Try to configure the next state if needed.
466         notifyPolicyIfNeeded();
467     }
468 
notifyDeviceStateInfoChanged()469     private void notifyDeviceStateInfoChanged() {
470         if (Thread.holdsLock(mLock)) {
471             throw new IllegalStateException(
472                     "Attempting to notify callbacks with service lock held.");
473         }
474 
475         // Grab the lock and copy the process records and the current info.
476         ArrayList<ProcessRecord> registeredProcesses;
477         DeviceStateInfo info;
478         synchronized (mLock) {
479             if (mProcessRecords.size() == 0) {
480                 return;
481             }
482 
483             registeredProcesses = new ArrayList<>();
484             for (int i = 0; i < mProcessRecords.size(); i++) {
485                 registeredProcesses.add(mProcessRecords.valueAt(i));
486             }
487 
488             info = getDeviceStateInfoLocked();
489         }
490 
491         // After releasing the lock, send the notifications out.
492         for (int i = 0; i < registeredProcesses.size(); i++) {
493             registeredProcesses.get(i).notifyDeviceStateInfoAsync(info);
494         }
495     }
496 
497     /**
498      * Notifies all dirty requests (requests that have a change in status, but have not yet been
499      * notified) that their status has changed.
500      */
notifyRequestsOfStatusChangeIfNeeded()501     private void notifyRequestsOfStatusChangeIfNeeded() {
502         if (Thread.holdsLock(mLock)) {
503             throw new IllegalStateException(
504                     "Attempting to notify requests with service lock held.");
505         }
506 
507         ArraySet<OverrideRequestRecord> dirtyRequests;
508         synchronized (mLock) {
509             if (mRequestsPendingStatusChange.isEmpty()) {
510                 return;
511             }
512 
513             dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange);
514             mRequestsPendingStatusChange.clear();
515         }
516 
517         // After releasing the lock, send the notifications out.
518         for (int i = 0; i < dirtyRequests.size(); i++) {
519             dirtyRequests.valueAt(i).notifyStatusIfNeeded();
520         }
521     }
522 
registerProcess(int pid, IDeviceStateManagerCallback callback)523     private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
524         DeviceStateInfo currentInfo;
525         ProcessRecord record;
526         // Grab the lock to register the callback and get the current state.
527         synchronized (mLock) {
528             if (mProcessRecords.contains(pid)) {
529                 throw new SecurityException("The calling process has already registered an"
530                         + " IDeviceStateManagerCallback.");
531             }
532 
533             record = new ProcessRecord(callback, pid);
534             try {
535                 callback.asBinder().linkToDeath(record, 0);
536             } catch (RemoteException ex) {
537                 throw new RuntimeException(ex);
538             }
539             mProcessRecords.put(pid, record);
540 
541             currentInfo = mCommittedState.isPresent() ? getDeviceStateInfoLocked() : null;
542         }
543 
544         if (currentInfo != null) {
545             // If there is not a committed state we'll wait to notify the process of the initial
546             // value.
547             record.notifyDeviceStateInfoAsync(currentInfo);
548         }
549     }
550 
handleProcessDied(ProcessRecord processRecord)551     private void handleProcessDied(ProcessRecord processRecord) {
552         synchronized (mLock) {
553             // Cancel all requests from this process.
554             final int requestCount = processRecord.mRequestRecords.size();
555             for (int i = 0; i < requestCount; i++) {
556                 final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i);
557                 // Cancel the request but don't mark it as dirty since there's no need to send
558                 // notifications if the process has died.
559                 request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED,
560                         false /* markDirty */);
561             }
562 
563             mProcessRecords.remove(processRecord.mPid);
564 
565             updatePendingStateLocked();
566         }
567 
568         notifyPolicyIfNeeded();
569     }
570 
requestStateInternal(int state, int flags, int callingPid, @NonNull IBinder token)571     private void requestStateInternal(int state, int flags, int callingPid,
572             @NonNull IBinder token) {
573         synchronized (mLock) {
574             final ProcessRecord processRecord = mProcessRecords.get(callingPid);
575             if (processRecord == null) {
576                 throw new IllegalStateException("Process " + callingPid
577                         + " has no registered callback.");
578             }
579 
580             if (processRecord.mRequestRecords.get(token) != null) {
581                 throw new IllegalStateException("Request has already been made for the supplied"
582                         + " token: " + token);
583             }
584 
585             final Optional<DeviceState> deviceState = getStateLocked(state);
586             if (!deviceState.isPresent()) {
587                 throw new IllegalArgumentException("Requested state: " + state
588                         + " is not supported.");
589             }
590 
591             OverrideRequestRecord topRecord = mRequestRecords.isEmpty()
592                     ? null : mRequestRecords.get(mRequestRecords.size() - 1);
593             if (topRecord != null) {
594                 topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED);
595             }
596 
597             final OverrideRequestRecord request =
598                     new OverrideRequestRecord(processRecord, token, deviceState.get(), flags);
599             mRequestRecords.add(request);
600             processRecord.mRequestRecords.put(request.mToken, request);
601 
602             final boolean updatedPendingState = updatePendingStateLocked();
603             if (!updatedPendingState && !mPendingState.isPresent()) {
604                 // We don't set the status of the new request to ACTIVE if the request updated the
605                 // pending state as it will be set in commitPendingState().
606                 request.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE, true /* markDirty */);
607             }
608         }
609 
610         notifyRequestsOfStatusChangeIfNeeded();
611         notifyPolicyIfNeeded();
612     }
613 
cancelRequestInternal(int callingPid, @NonNull IBinder token)614     private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
615         synchronized (mLock) {
616             final ProcessRecord processRecord = mProcessRecords.get(callingPid);
617             if (processRecord == null) {
618                 throw new IllegalStateException("Process " + callingPid
619                         + " has no registered callback.");
620             }
621 
622             OverrideRequestRecord request = processRecord.mRequestRecords.get(token);
623             if (request == null) {
624                 throw new IllegalStateException("No known request for the given token");
625             }
626 
627             request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
628 
629             updatePendingStateLocked();
630         }
631 
632         notifyRequestsOfStatusChangeIfNeeded();
633         notifyPolicyIfNeeded();
634     }
635 
dumpInternal(PrintWriter pw)636     private void dumpInternal(PrintWriter pw) {
637         pw.println("DEVICE STATE MANAGER (dumpsys device_state)");
638 
639         synchronized (mLock) {
640             pw.println("  mCommittedState=" + mCommittedState);
641             pw.println("  mPendingState=" + mPendingState);
642             pw.println("  mBaseState=" + mBaseState);
643             pw.println("  mOverrideState=" + getOverrideState());
644 
645             final int processCount = mProcessRecords.size();
646             pw.println();
647             pw.println("Registered processes: size=" + processCount);
648             for (int i = 0; i < processCount; i++) {
649                 ProcessRecord processRecord = mProcessRecords.valueAt(i);
650                 pw.println("  " + i + ": mPid=" + processRecord.mPid);
651             }
652 
653             final int requestCount = mRequestRecords.size();
654             pw.println();
655             pw.println("Override requests: size=" + requestCount);
656             for (int i = 0; i < requestCount; i++) {
657                 OverrideRequestRecord requestRecord = mRequestRecords.get(i);
658                 pw.println("  " + i + ": mPid=" + requestRecord.mProcessRecord.mPid
659                         + ", mRequestedState=" + requestRecord.mRequestedState
660                         + ", mFlags=" + requestRecord.mFlags
661                         + ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus));
662             }
663         }
664     }
665 
666     private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
667         @Override
onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates)668         public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
669             if (newDeviceStates.length == 0) {
670                 throw new IllegalArgumentException("Supported device states must not be empty");
671             }
672             updateSupportedStates(newDeviceStates);
673         }
674 
675         @Override
onStateChanged( @ntRangefrom = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier)676         public void onStateChanged(
677                 @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier) {
678             if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
679                 throw new IllegalArgumentException("Invalid identifier: " + identifier);
680             }
681 
682             setBaseState(identifier);
683         }
684     }
685 
686     private final class ProcessRecord implements IBinder.DeathRecipient {
687         private final IDeviceStateManagerCallback mCallback;
688         private final int mPid;
689 
690         private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>();
691 
ProcessRecord(IDeviceStateManagerCallback callback, int pid)692         ProcessRecord(IDeviceStateManagerCallback callback, int pid) {
693             mCallback = callback;
694             mPid = pid;
695         }
696 
697         @Override
binderDied()698         public void binderDied() {
699             handleProcessDied(this);
700         }
701 
notifyDeviceStateInfoAsync(@onNull DeviceStateInfo info)702         public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) {
703             try {
704                 mCallback.onDeviceStateInfoChanged(info);
705             } catch (RemoteException ex) {
706                 Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.",
707                         ex);
708             }
709         }
710 
notifyRequestActiveAsync(OverrideRequestRecord request)711         public void notifyRequestActiveAsync(OverrideRequestRecord request) {
712             try {
713                 mCallback.onRequestActive(request.mToken);
714             } catch (RemoteException ex) {
715                 Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
716                         ex);
717             }
718         }
719 
notifyRequestSuspendedAsync(OverrideRequestRecord request)720         public void notifyRequestSuspendedAsync(OverrideRequestRecord request) {
721             try {
722                 mCallback.onRequestSuspended(request.mToken);
723             } catch (RemoteException ex) {
724                 Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
725                         ex);
726             }
727         }
728 
notifyRequestCanceledAsync(OverrideRequestRecord request)729         public void notifyRequestCanceledAsync(OverrideRequestRecord request) {
730             try {
731                 mCallback.onRequestCanceled(request.mToken);
732             } catch (RemoteException ex) {
733                 Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
734                         ex);
735             }
736         }
737     }
738 
739     /** A record describing a request to override the state of the device. */
740     private final class OverrideRequestRecord {
741         public static final int STATUS_UNKNOWN = 0;
742         public static final int STATUS_ACTIVE = 1;
743         public static final int STATUS_SUSPENDED = 2;
744         public static final int STATUS_CANCELED = 3;
745 
746         @Nullable
statusToString(int status)747         public String statusToString(int status) {
748             switch (status) {
749                 case STATUS_ACTIVE:
750                     return "ACTIVE";
751                 case STATUS_SUSPENDED:
752                     return "SUSPENDED";
753                 case STATUS_CANCELED:
754                     return "CANCELED";
755                 case STATUS_UNKNOWN:
756                     return "UNKNOWN";
757                 default:
758                     return null;
759             }
760         }
761 
762         private final ProcessRecord mProcessRecord;
763         @NonNull
764         private final IBinder mToken;
765         @NonNull
766         private final DeviceState mRequestedState;
767         private final int mFlags;
768 
769         private int mStatus = STATUS_UNKNOWN;
770         private int mLastNotifiedStatus = STATUS_UNKNOWN;
771 
OverrideRequestRecord(@onNull ProcessRecord processRecord, @NonNull IBinder token, @NonNull DeviceState requestedState, int flags)772         OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token,
773                 @NonNull DeviceState requestedState, int flags) {
774             mProcessRecord = processRecord;
775             mToken = token;
776             mRequestedState = requestedState;
777             mFlags = flags;
778         }
779 
setStatusLocked(int status)780         public void setStatusLocked(int status) {
781             setStatusLocked(status, true /* markDirty */);
782         }
783 
setStatusLocked(int status, boolean markDirty)784         public void setStatusLocked(int status, boolean markDirty) {
785             if (mStatus != status) {
786                 if (mStatus == STATUS_CANCELED) {
787                     throw new IllegalStateException(
788                             "Can not alter the status of a request after set to CANCELED.");
789                 }
790 
791                 mStatus = status;
792 
793                 if (mStatus == STATUS_CANCELED) {
794                     mRequestRecords.remove(this);
795                     mProcessRecord.mRequestRecords.remove(mToken);
796                 }
797 
798                 if (markDirty) {
799                     mRequestsPendingStatusChange.add(this);
800                 }
801             }
802         }
803 
notifyStatusIfNeeded()804         public void notifyStatusIfNeeded() {
805             int stateToReport;
806             synchronized (mLock) {
807                 if (mLastNotifiedStatus == mStatus) {
808                     return;
809                 }
810 
811                 stateToReport = mStatus;
812                 mLastNotifiedStatus = mStatus;
813             }
814 
815             if (stateToReport == STATUS_ACTIVE) {
816                 mProcessRecord.notifyRequestActiveAsync(this);
817             } else if (stateToReport == STATUS_SUSPENDED) {
818                 mProcessRecord.notifyRequestSuspendedAsync(this);
819             } else if (stateToReport == STATUS_CANCELED) {
820                 mProcessRecord.notifyRequestCanceledAsync(this);
821             }
822         }
823     }
824 
825     /** Implementation of {@link IDeviceStateManager} published as a binder service. */
826     private final class BinderService extends IDeviceStateManager.Stub {
827         @Override // Binder call
getDeviceStateInfo()828         public DeviceStateInfo getDeviceStateInfo() {
829             synchronized (mLock) {
830                 return getDeviceStateInfoLocked();
831             }
832         }
833 
834         @Override // Binder call
registerCallback(IDeviceStateManagerCallback callback)835         public void registerCallback(IDeviceStateManagerCallback callback) {
836             if (callback == null) {
837                 throw new IllegalArgumentException("Device state callback must not be null.");
838             }
839 
840             final int callingPid = Binder.getCallingPid();
841             final long token = Binder.clearCallingIdentity();
842             try {
843                 registerProcess(callingPid, callback);
844             } finally {
845                 Binder.restoreCallingIdentity(token);
846             }
847         }
848 
849         @Override // Binder call
requestState(IBinder token, int state, int flags)850         public void requestState(IBinder token, int state, int flags) {
851             getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
852                     "Permission required to request device state.");
853 
854             if (token == null) {
855                 throw new IllegalArgumentException("Request token must not be null.");
856             }
857 
858             final int callingPid = Binder.getCallingPid();
859             final long callingIdentity = Binder.clearCallingIdentity();
860             try {
861                 requestStateInternal(state, flags, callingPid, token);
862             } finally {
863                 Binder.restoreCallingIdentity(callingIdentity);
864             }
865         }
866 
867         @Override // Binder call
cancelRequest(IBinder token)868         public void cancelRequest(IBinder token) {
869             getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
870                     "Permission required to clear requested device state.");
871 
872             if (token == null) {
873                 throw new IllegalArgumentException("Request token must not be null.");
874             }
875 
876             final int callingPid = Binder.getCallingPid();
877             final long callingIdentity = Binder.clearCallingIdentity();
878             try {
879                 cancelRequestInternal(callingPid, token);
880             } finally {
881                 Binder.restoreCallingIdentity(callingIdentity);
882             }
883         }
884 
885         @Override // Binder call
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver result)886         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
887                 String[] args, ShellCallback callback, ResultReceiver result) {
888             new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
889                     .exec(this, in, out, err, args, callback, result);
890         }
891 
892         @Override // Binder call
dump(FileDescriptor fd, final PrintWriter pw, String[] args)893         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
894             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
895 
896             final long token = Binder.clearCallingIdentity();
897             try {
898                 dumpInternal(pw);
899             } finally {
900                 Binder.restoreCallingIdentity(token);
901             }
902         }
903     }
904 }
905