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 com.android.server.app;
18 
19 
20 import android.annotation.Nullable;
21 import android.os.IInterface;
22 
23 import com.android.internal.infra.AndroidFuture;
24 import com.android.internal.infra.ServiceConnector;
25 
26 import java.util.concurrent.CompletableFuture;
27 
28 /**
29  * Fake implementation of {@link ServiceConnector<T>} used for tests.
30  *
31  * Tests provide a service instance via {@link #FakeServiceConnector(IInterface)} that will be
32  * connected to and used to fulfill service jobs.
33  */
34 final class FakeServiceConnector<T extends IInterface> implements ServiceConnector<T> {
35     private final T mService;
36     @Nullable
37     private ServiceLifecycleCallbacks mServiceLifecycleCallbacks;
38     private boolean mIsConnected;
39     private int mConnectCount = 0;
40 
FakeServiceConnector(T service)41     FakeServiceConnector(T service) {
42         mService = service;
43     }
44 
45     @Override
run(VoidJob<T> job)46     public boolean run(VoidJob<T> job) {
47         AndroidFuture<Void> unusedFuture = post(job);
48         return true;
49     }
50 
51     @Override
post(VoidJob<T> job)52     public AndroidFuture<Void> post(VoidJob<T> job) {
53         markPossibleConnection();
54 
55         return postForResult(job);
56     }
57 
58     @Override
postForResult(Job<T, R> job)59     public <R> AndroidFuture<R> postForResult(Job<T, R> job) {
60         markPossibleConnection();
61 
62         AndroidFuture<R> androidFuture = new AndroidFuture();
63         try {
64             androidFuture.complete(job.run(mService));
65         } catch (Exception ex) {
66             androidFuture.completeExceptionally(ex);
67         }
68         return androidFuture;
69     }
70 
71     @Override
72     @SuppressWarnings("FutureReturnValueIgnored")
postAsync(Job<T, CompletableFuture<R>> job)73     public <R> AndroidFuture<R> postAsync(Job<T, CompletableFuture<R>> job) {
74         markPossibleConnection();
75         AndroidFuture<R> androidFuture = new AndroidFuture();
76 
77         try {
78             CompletableFuture<R> future = job.run(mService);
79             future.whenComplete((result, exception) -> {
80                 if (exception != null) {
81                     androidFuture.completeExceptionally(exception);
82                 } else {
83                     androidFuture.complete(result);
84                 }
85             });
86         } catch (Exception ex) {
87             androidFuture.completeExceptionally(ex);
88         }
89 
90         return androidFuture;
91     }
92 
93     @Override
connect()94     public AndroidFuture<T> connect() {
95         markPossibleConnection();
96         return AndroidFuture.completedFuture(mService);
97     }
98 
99     @Override
unbind()100     public void unbind() {
101         if (mServiceLifecycleCallbacks != null) {
102             mServiceLifecycleCallbacks.onDisconnected(mService);
103         }
104         mIsConnected = false;
105     }
106 
107     @Override
setServiceLifecycleCallbacks(@ullable ServiceLifecycleCallbacks<T> callbacks)108     public void setServiceLifecycleCallbacks(@Nullable ServiceLifecycleCallbacks<T> callbacks) {
109         mServiceLifecycleCallbacks = callbacks;
110     }
111 
markPossibleConnection()112     private void markPossibleConnection() {
113         if (mIsConnected) {
114             return;
115         }
116 
117         mConnectCount += 1;
118         mIsConnected = true;
119 
120         if (mServiceLifecycleCallbacks != null) {
121             mServiceLifecycleCallbacks.onConnected(mService);
122         }
123     }
124 
killServiceProcess()125     public void killServiceProcess() {
126         if (!mIsConnected) {
127             return;
128         }
129         mIsConnected = false;
130 
131         if (mServiceLifecycleCallbacks != null) {
132             mServiceLifecycleCallbacks.onBinderDied();
133         }
134     }
135 
136     /**
137      * Returns {@code true} if the underlying service is connected.
138      */
getIsConnected()139     public boolean getIsConnected() {
140         return mIsConnected;
141     }
142 
143     /**
144      * Returns the number of times a connection was established with the underlying service.
145      */
getConnectCount()146     public int getConnectCount() {
147         return mConnectCount;
148     }
149 }
150