1 /*
2  * Copyright (C) 2021 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.hardware.camera2.impl;
18 
19 import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
20 
21 import android.hardware.camera2.CameraInjectionSession;
22 import android.hardware.camera2.ICameraInjectionCallback;
23 import android.hardware.camera2.ICameraInjectionSession;
24 import android.os.Binder;
25 import android.os.IBinder;
26 import android.os.RemoteException;
27 import android.util.Log;
28 
29 import java.util.concurrent.Executor;
30 
31 
32 /**
33  * The class inherits CameraInjectionSession. Use CameraManager#injectCamera to instantiate.
34  */
35 public class CameraInjectionSessionImpl extends CameraInjectionSession
36         implements IBinder.DeathRecipient {
37     private static final String TAG = "CameraInjectionSessionImpl";
38 
39     private final CameraInjectionCallback mCallback = new CameraInjectionCallback();
40     private final CameraInjectionSession.InjectionStatusCallback mInjectionStatusCallback;
41     private final Executor mExecutor;
42     private final Object mInterfaceLock = new Object();
43     private ICameraInjectionSession mInjectionSession;
44 
CameraInjectionSessionImpl(InjectionStatusCallback callback, Executor executor)45     public CameraInjectionSessionImpl(InjectionStatusCallback callback, Executor executor) {
46         mInjectionStatusCallback = callback;
47         mExecutor = executor;
48     }
49 
50     @Override
close()51     public void close() {
52         synchronized (mInterfaceLock) {
53             try {
54                 if (mInjectionSession != null) {
55                     mInjectionSession.stopInjection();
56                     mInjectionSession.asBinder().unlinkToDeath(this, /*flags*/0);
57                     mInjectionSession = null;
58                 }
59             } catch (RemoteException e) {
60                 // Ignore binder errors for disconnect
61             }
62         }
63     }
64 
65     @Override
finalize()66     protected void finalize() throws Throwable {
67         try {
68             close();
69         } finally {
70             super.finalize();
71         }
72     }
73 
74     @Override
binderDied()75     public void binderDied() {
76         synchronized (mInterfaceLock) {
77             Log.w(TAG, "CameraInjectionSessionImpl died unexpectedly");
78 
79             if (mInjectionSession == null) {
80                 return; // CameraInjectionSession already closed
81             }
82 
83             Runnable r = new Runnable() {
84                 @Override
85                 public void run() {
86                     mInjectionStatusCallback.onInjectionError(
87                             CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_SERVICE);
88                 }
89             };
90             final long ident = Binder.clearCallingIdentity();
91             try {
92                 CameraInjectionSessionImpl.this.mExecutor.execute(r);
93             } finally {
94                 Binder.restoreCallingIdentity(ident);
95             }
96         }
97     }
98 
getCallback()99     public CameraInjectionCallback getCallback() {
100         return mCallback;
101     }
102 
103     /**
104      * Set remote injection session, which triggers initial onInjectionSucceeded callbacks.
105      *
106      * <p>This function may post onInjectionError if remoteInjectionSession dies
107      * during injecting.</p>
108      */
setRemoteInjectionSession(ICameraInjectionSession injectionSession)109     public void setRemoteInjectionSession(ICameraInjectionSession injectionSession) {
110         synchronized (mInterfaceLock) {
111             if (injectionSession == null) {
112                 Log.e(TAG, "The camera injection session has encountered a serious error");
113                 scheduleNotifyError(
114                         CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_SESSION);
115                 return;
116             }
117 
118             mInjectionSession = injectionSession;
119 
120             IBinder remoteSessionBinder = injectionSession.asBinder();
121             if (remoteSessionBinder == null) {
122                 Log.e(TAG, "The camera injection session has encountered a serious error");
123                 scheduleNotifyError(
124                         CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_SESSION);
125                 return;
126             }
127 
128             final long ident = Binder.clearCallingIdentity();
129             try {
130                 remoteSessionBinder.linkToDeath(this, /*flag*/ 0);
131                 mExecutor.execute(new Runnable() {
132                     @Override
133                     public void run() {
134                         mInjectionStatusCallback
135                                 .onInjectionSucceeded(CameraInjectionSessionImpl.this);
136                     }
137                 });
138             } catch (RemoteException e) {
139                 scheduleNotifyError(
140                         CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_SESSION);
141             } finally {
142                 Binder.restoreCallingIdentity(ident);
143             }
144         }
145     }
146 
147     /**
148      * The method called when the injection camera has encountered a serious error.
149      *
150      * @param errorCode The error code.
151      * @see #ERROR_INJECTION_SESSION
152      * @see #ERROR_INJECTION_SERVICE
153      * @see #ERROR_INJECTION_UNSUPPORTED
154      */
onInjectionError(final int errorCode)155     public void onInjectionError(final int errorCode) {
156         Log.v(TAG, String.format(
157                 "Injection session error received, code %d", errorCode));
158 
159         synchronized (mInterfaceLock) {
160             if (mInjectionSession == null) {
161                 return; // mInjectionSession already closed
162             }
163 
164             switch (errorCode) {
165                 case CameraInjectionCallback.ERROR_INJECTION_SESSION:
166                     scheduleNotifyError(
167                             CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_SESSION);
168                     break;
169                 case CameraInjectionCallback.ERROR_INJECTION_SERVICE:
170                     scheduleNotifyError(
171                             CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_SERVICE);
172                     break;
173                 case CameraInjectionCallback.ERROR_INJECTION_UNSUPPORTED:
174                     scheduleNotifyError(
175                             CameraInjectionSession.InjectionStatusCallback
176                                     .ERROR_INJECTION_UNSUPPORTED);
177                     break;
178                 default:
179                     Log.e(TAG, "Unknown error from injection session: " + errorCode);
180                     scheduleNotifyError(
181                             CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_SERVICE);
182             }
183         }
184     }
185 
scheduleNotifyError(final int errorCode)186     private void scheduleNotifyError(final int errorCode) {
187         final long ident = Binder.clearCallingIdentity();
188         try {
189             mExecutor.execute(obtainRunnable(
190                     CameraInjectionSessionImpl::notifyError,
191                     this, errorCode).recycleOnUse());
192         } finally {
193             Binder.restoreCallingIdentity(ident);
194         }
195     }
196 
notifyError(final int errorCode)197     private void notifyError(final int errorCode) {
198         if (mInjectionSession != null) {
199             mInjectionStatusCallback.onInjectionError(errorCode);
200         }
201     }
202 
203     /**
204      * The class inherits ICameraInjectionCallbacks.Stub. Use CameraManager#injectCamera to
205      * instantiate.
206      */
207     public class CameraInjectionCallback extends ICameraInjectionCallback.Stub {
208 
209         @Override
asBinder()210         public IBinder asBinder() {
211             return this;
212         }
213 
214         @Override
onInjectionError(int errorCode)215         public void onInjectionError(int errorCode) {
216             CameraInjectionSessionImpl.this.onInjectionError(errorCode);
217         }
218     }
219 }
220