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