• 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 android.service.rotationresolver;
18 
19 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
20 
21 import android.annotation.IntDef;
22 import android.annotation.MainThread;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.SystemApi;
26 import android.app.Service;
27 import android.content.Intent;
28 import android.os.CancellationSignal;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.ICancellationSignal;
32 import android.os.Looper;
33 import android.os.RemoteException;
34 import android.os.SystemClock;
35 import android.view.Surface;
36 
37 import java.lang.annotation.Retention;
38 import java.lang.annotation.RetentionPolicy;
39 import java.util.Objects;
40 
41 /**
42  * Abstract base class for rotation resolver service.
43  *
44  * <p> A {@link RotationResolverService} is a service that help determine screen rotation for the
45  * system. When the system wants to resolve rotations, it will send a request to this service
46  * via {@link #onResolveRotation} interface. A {@link RotationResolverCallback} is
47  * attached to the request so that the implementer of the rotation resolver service can send
48  * back results to the system. The system may then decide to rotate the screen based on the
49  * results.
50  *
51  * <p> If RotationResolverService provides the result in time, the system will respect that result
52  * and rotate the screen if possible.
53  *
54  * <p> The system's default RotationResolverService implementation is configured at
55  * the {@code config_defaultRotationResolverService} field in the config XML file.
56  *
57  * <p> The implementation of RotationResolverService must have the following service interface.
58  * Also, it must have permission {@link android.Manifest.permission#BIND_ROTATION_RESOLVER_SERVICE}.
59  *
60  * <pre>
61  * {@literal
62  * <service android:name=".RotationResolverService"
63  *          android:permission="android.permission.BIND_ROTATION_RESOLVER_SERVICE">
64  * </service>}
65  * </pre>
66  *
67  * @hide
68  */
69 @SystemApi
70 public abstract class RotationResolverService extends Service {
71     /**
72      * The {@link Intent} that must be declared as handled by the service.
73      * To be supported, the service must also require the
74      * {@link android.Manifest.permission#BIND_ROTATION_RESOLVER_SERVICE} permission so
75      * that other applications can not abuse it.
76      */
77     public static final String SERVICE_INTERFACE =
78             "android.service.rotationresolver.RotationResolverService";
79 
80     /** Request has been cancelled. */
81     public static final int ROTATION_RESULT_FAILURE_CANCELLED = 0;
82 
83     /** Request timed out. */
84     public static final int ROTATION_RESULT_FAILURE_TIMED_OUT = 1;
85 
86     /** Preempted by other requests. */
87     public static final int ROTATION_RESULT_FAILURE_PREEMPTED = 2;
88 
89     /** Unknown reasons for failing to fulfill the request. */
90     public static final int ROTATION_RESULT_FAILURE_UNKNOWN = 3;
91 
92     /** Does not support rotation query at this moment. */
93     public static final int ROTATION_RESULT_FAILURE_NOT_SUPPORTED = 4;
94 
95     /**
96      * Result codes explaining why rotation recommendation request was not successful.
97      *
98      * @hide
99      */
100     @IntDef(prefix = {"ROTATION_RESULT_FAILURE_"}, value = {
101             ROTATION_RESULT_FAILURE_CANCELLED,
102             ROTATION_RESULT_FAILURE_TIMED_OUT, ROTATION_RESULT_FAILURE_PREEMPTED,
103             ROTATION_RESULT_FAILURE_UNKNOWN,
104             ROTATION_RESULT_FAILURE_NOT_SUPPORTED})
105     @Retention(RetentionPolicy.SOURCE)
106     public @interface FailureCodes {
107     }
108 
109     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper(), null, true);
110     @Nullable
111     private RotationResolverCallbackWrapper mPendingCallback;
112     @Nullable
113     private CancellationSignal mCancellationSignal;
114 
115     @Nullable
116     @Override
onBind(@onNull Intent intent)117     public final IBinder onBind(@NonNull Intent intent) {
118         if (SERVICE_INTERFACE.equals(intent.getAction())) {
119             return new IRotationResolverService.Stub() {
120                 /** {@inheritDoc} */
121                 @Override
122                 public void resolveRotation(IRotationResolverCallback callback,
123                         RotationResolutionRequest request) throws RemoteException {
124                     Objects.requireNonNull(callback);
125                     Objects.requireNonNull(request);
126                     final ICancellationSignal transport = CancellationSignal.createTransport();
127                     callback.onCancellable(transport);
128                     mMainThreadHandler.sendMessage(
129                             obtainMessage(RotationResolverService::resolveRotation,
130                                     RotationResolverService.this, callback, request, transport));
131                 }
132             };
133         }
134         return null;
135     }
136 
137     @MainThread
138     private void resolveRotation(IRotationResolverCallback callback,
139             RotationResolutionRequest request, ICancellationSignal transport) {
140         // If there is a valid, uncancelled pending callback running in process, the new rotation
141         // resolution request will be rejected immediately with a failure result.
142         if (mPendingCallback != null
143                 && (mCancellationSignal == null || !mCancellationSignal.isCanceled())
144                 && (SystemClock.uptimeMillis() < mPendingCallback.mExpirationTime)) {
145             reportFailures(callback, ROTATION_RESULT_FAILURE_PREEMPTED);
146             return;
147         }
148         mPendingCallback = new RotationResolverCallbackWrapper(callback, this,
149                 SystemClock.uptimeMillis() + request.getTimeoutMillis());
150         mCancellationSignal = CancellationSignal.fromTransport(transport);
151 
152         onResolveRotation(request, mCancellationSignal, mPendingCallback);
153     }
154 
155     @MainThread
156     private void sendRotationResult(IRotationResolverCallback internalCallback, int result) {
157         if (mPendingCallback != null && mPendingCallback.mCallback == internalCallback) {
158             mPendingCallback = null;
159             try {
160                 internalCallback.onSuccess(result);
161             } catch (RemoteException e) {
162                 e.rethrowFromSystemServer();
163             }
164         }
165     }
166 
167     @MainThread
168     private void sendFailureResult(IRotationResolverCallback internalCallback, int error) {
169         if (mPendingCallback != null && internalCallback == mPendingCallback.mCallback) {
170             reportFailures(internalCallback, error);
171             mPendingCallback = null;
172         }
173     }
174 
175     @MainThread
176     private void reportFailures(IRotationResolverCallback callback, int error) {
177         try {
178             callback.onFailure(error);
179         } catch (RemoteException e) {
180             e.rethrowFromSystemServer();
181         }
182     }
183 
184 
185     /**
186      * Gets called when the system requests to resolve the screen rotation. The implementer then
187      * should return the result via the provided callback.
188      *
189      * @param request A request instance that contains information from the system that may help
190      *                the implementer provide a better result.
191      * @param cancellationSignal The signal for observing the cancellation of the request. The
192      *                           system will use this to notify the implementer that the rotation
193      *                           result is no longer needed. Implementer should then stop handling
194      *                           the request in order to save resources.
195      * @param callback A callback that Receives the rotation results.
196      */
197     public abstract void onResolveRotation(@NonNull RotationResolutionRequest request,
198             @Nullable CancellationSignal cancellationSignal,
199             @NonNull RotationResolverCallback callback);
200 
201     /**
202      * Interface definition for a callback to be invoked when rotation resolution request is
203      * completed.
204      */
205     public interface RotationResolverCallback {
206         /**
207          * Signals a success and provides the result code.
208          */
209         void onSuccess(@Surface.Rotation int result);
210 
211         /**
212          * Signals a failure and provides the error code.
213          */
214         void onFailure(@FailureCodes int error);
215     }
216 
217 
218     /**
219      * An implementation of the callback that receives rotation resolution results.
220      *
221      * @hide
222      */
223     public static final class RotationResolverCallbackWrapper implements RotationResolverCallback {
224 
225         @NonNull
226         private final android.service.rotationresolver.IRotationResolverCallback mCallback;
227         @NonNull
228         private final RotationResolverService mService;
229         @NonNull
230         private final Handler mHandler;
231 
232         private final long mExpirationTime;
233 
234         private RotationResolverCallbackWrapper(
235                 @NonNull android.service.rotationresolver.IRotationResolverCallback callback,
236                 RotationResolverService service, long expirationTime) {
237             mCallback = callback;
238             mService = service;
239             mHandler = service.mMainThreadHandler;
240             mExpirationTime = expirationTime;
241             Objects.requireNonNull(mHandler);
242         }
243 
244 
245         @Override
246         public void onSuccess(int result) {
247             mHandler.sendMessage(
248                     obtainMessage(RotationResolverService::sendRotationResult, mService, mCallback,
249                             result));
250         }
251 
252         @Override
253         public void onFailure(int error) {
254             mHandler.sendMessage(
255                     obtainMessage(RotationResolverService::sendFailureResult, mService,
256                             mCallback,
257                             error));
258         }
259     }
260 }
261