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