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.content.pm.cts;
18 
19 import static android.system.OsConstants.EAGAIN;
20 import static android.system.OsConstants.EINTR;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertNull;
24 
25 import android.annotation.NonNull;
26 import android.os.Bundle;
27 import android.os.ParcelFileDescriptor;
28 import android.os.RemoteException;
29 import android.os.ResultReceiver;
30 import android.os.ServiceManager;
31 import android.system.ErrnoException;
32 import android.system.Os;
33 import android.system.OsConstants;
34 import android.system.StructPollfd;
35 import android.util.Log;
36 import android.util.Slog;
37 
38 import com.android.incfs.install.IDeviceConnection;
39 import com.android.incfs.install.ILogger;
40 
41 import libcore.io.IoUtils;
42 
43 import java.io.ByteArrayOutputStream;
44 import java.io.FileDescriptor;
45 import java.io.FileInputStream;
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.nio.ByteBuffer;
49 
50 public class IncrementalDeviceConnection implements IDeviceConnection {
51     private static final String TAG = "IncrementalDeviceConnection";
52     private static final boolean DEBUG = false;
53 
54     private static final int POLL_TIMEOUT_MS = 300000;
55 
56     private enum ConnectionType {
57         RELIABLE,
58         UNRELIABLE,
59     }
60 
61     private ConnectionType mConnectionType;
62     private final Thread mShellCommand;
63     private final ParcelFileDescriptor mPfd;
64     private final FileDescriptor mFd;
65     private final StructPollfd[] mPollfds = new StructPollfd[]{
66             new StructPollfd()
67     };
68 
IncrementalDeviceConnection(ConnectionType connectionType, Thread shellCommand, ParcelFileDescriptor pfd)69     private IncrementalDeviceConnection(ConnectionType connectionType, Thread shellCommand,
70             ParcelFileDescriptor pfd) {
71         mConnectionType = connectionType;
72         mShellCommand = shellCommand;
73         mPfd = pfd;
74         mFd = pfd.getFileDescriptor();
75 
76         mPollfds[0].fd = mFd;
77         mPollfds[0].events = (short) OsConstants.POLLIN;
78         mPollfds[0].revents = 0;
79         mPollfds[0].userData = null;
80 
81         mShellCommand.start();
82     }
83 
84     @Override
read(ByteBuffer buffer, long timeoutMs)85     public int read(ByteBuffer buffer, long timeoutMs) throws IOException {
86         final boolean blocking = buffer.position() == 0;
87         while (true) {
88             try {
89                 int res = Os.poll(mPollfds, blocking ? POLL_TIMEOUT_MS : 0);
90                 if (res < 0) {
91                     return res;
92                 }
93                 if (res == 0) {
94                     if (blocking) {
95                         throw new IOException("timeout");
96                     }
97                     return 0;
98                 }
99                 return Os.read(mFd, buffer);
100             } catch (ErrnoException e) {
101                 if (e.errno == EINTR) {
102                     continue;
103                 }
104                 if (mConnectionType == ConnectionType.UNRELIABLE) {
105                     e.rethrowAsIOException();
106                 }
107                 if (e.errno == EAGAIN) {
108                     if (!blocking) {
109                         return 0;
110                     }
111                     continue;
112                 }
113                 e.rethrowAsIOException();
114             }
115         }
116     }
117 
118     @Override
write(ByteBuffer buffer, long timeoutMs)119     public int write(ByteBuffer buffer, long timeoutMs) throws IOException {
120         try {
121             return Os.write(mFd, buffer);
122         } catch (ErrnoException e) {
123             e.rethrowAsIOException();
124         }
125         return 0;
126     }
127 
128     @Override
close()129     public void close() throws Exception {
130         mShellCommand.join();
131         IoUtils.closeQuietly(mPfd);
132     }
133 
134     static class Logger implements ILogger {
135         @Override
error(Throwable t, String msgFormat, Object... args)136         public void error(Throwable t, String msgFormat, Object... args) {
137             Slog.e(TAG, String.format(msgFormat, args), t);
138         }
139 
140         @Override
warning(String msgFormat, Object... args)141         public void warning(String msgFormat, Object... args) {
142             Slog.w(TAG, String.format(msgFormat, args));
143         }
144 
145         @Override
info(String msgFormat, Object... args)146         public void info(String msgFormat, Object... args) {
147             Slog.i(TAG, String.format(msgFormat, args));
148         }
149 
150         @Override
verbose(String msgFormat, Object... args)151         public void verbose(String msgFormat, Object... args) {
152             if (!DEBUG) {
153                 return;
154             }
155             Slog.v(TAG, String.format(msgFormat, args));
156         }
157     }
158 
159     static class Factory implements IDeviceConnection.Factory {
160         private final ConnectionType mConnectionType;
161         private final boolean mExpectInstallationSuccess;
162 
reliable()163         static Factory reliable() {
164             return new Factory(ConnectionType.RELIABLE, true);
165         }
166 
ureliable()167         static Factory ureliable() {
168             return new Factory(ConnectionType.UNRELIABLE, false);
169         }
170 
reliableExpectInstallationFailure()171         static Factory reliableExpectInstallationFailure() {
172             return new Factory(ConnectionType.RELIABLE, false);
173         }
174 
Factory(ConnectionType connectionType, boolean expectInstallationSuccess)175         private Factory(ConnectionType connectionType, boolean expectInstallationSuccess) {
176             mConnectionType = connectionType;
177             mExpectInstallationSuccess = expectInstallationSuccess;
178         }
179 
180         @Override
connectToService(@onNull String service, @NonNull String[] parameters)181         public IDeviceConnection connectToService(@NonNull String service,
182                 @NonNull String[] parameters) throws IOException {
183             ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createSocketPair();
184             IoUtils.setBlocking(pipe[0].getFileDescriptor(), false);
185             IoUtils.setBlocking(pipe[1].getFileDescriptor(), false);
186 
187             final ParcelFileDescriptor localPfd = pipe[0];
188             final ParcelFileDescriptor processPfd = pipe[1];
189 
190             final ResultReceiver resultReceiver;
191             if (mExpectInstallationSuccess) {
192                 resultReceiver = new ResultReceiver(null) {
193                     @Override
194                     protected void onReceiveResult(int resultCode, Bundle resultData) {
195                         if (resultCode == 0) {
196                             return;
197                         }
198                         final String message = readFullStreamOrError(
199                                 new FileInputStream(localPfd.getFileDescriptor()));
200                         assertEquals(message, 0, resultCode);
201                     }
202                 };
203             } else {
204                 resultReceiver = new ResultReceiver(null) {
205                     @Override
206                     protected void onReceiveResult(int resultCode, Bundle resultData) {
207                         if (resultCode == 0) {
208                             return;
209                         }
210                         final String message = readFullStreamOrError(
211                                 new FileInputStream(localPfd.getFileDescriptor()));
212                         Log.i(TAG, "Installation finished with code: " + resultCode + ", message: "
213                                 + message);
214                     }
215                 };
216             }
217 
218             final Thread shellCommand = new Thread(() -> {
219                 try {
220                     final FileDescriptor processFd = processPfd.getFileDescriptor();
221                     ServiceManager.getService(service).shellCommand(processFd, processFd, processFd,
222                             parameters, null, resultReceiver);
223                 } catch (RemoteException e) {
224                     if (mConnectionType == ConnectionType.RELIABLE) {
225                         assertNull(e);
226                     }
227                 } finally {
228                     IoUtils.closeQuietly(processPfd);
229                 }
230             });
231             return new IncrementalDeviceConnection(mConnectionType, shellCommand, localPfd);
232         }
233     }
234 
readFullStreamOrError(InputStream inputStream)235     private static String readFullStreamOrError(InputStream inputStream) {
236         try (ByteArrayOutputStream result = new ByteArrayOutputStream()) {
237             try {
238                 final byte[] buffer = new byte[1024];
239                 int length;
240                 while ((length = inputStream.read(buffer)) != -1) {
241                     result.write(buffer, 0, length);
242                 }
243             } catch (IOException e) {
244                 return result.toString("UTF-8") + " exception [" + e + "]";
245             }
246             return result.toString("UTF-8");
247         } catch (IOException e) {
248             return e.toString();
249         }
250     }
251 }
252