• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.os;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UnsupportedAppUsage;
22 import android.content.pm.ApplicationInfo;
23 import android.net.LocalSocket;
24 import android.net.LocalSocketAddress;
25 import android.util.Log;
26 import android.util.Slog;
27 
28 import com.android.internal.annotations.GuardedBy;
29 import com.android.internal.os.Zygote;
30 import com.android.internal.os.ZygoteConfig;
31 
32 import java.io.BufferedWriter;
33 import java.io.DataInputStream;
34 import java.io.IOException;
35 import java.io.OutputStreamWriter;
36 import java.nio.charset.StandardCharsets;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Base64;
40 import java.util.Collections;
41 import java.util.List;
42 import java.util.UUID;
43 
44 /*package*/ class ZygoteStartFailedEx extends Exception {
45     @UnsupportedAppUsage
ZygoteStartFailedEx(String s)46     ZygoteStartFailedEx(String s) {
47         super(s);
48     }
49 
50     @UnsupportedAppUsage
ZygoteStartFailedEx(Throwable cause)51     ZygoteStartFailedEx(Throwable cause) {
52         super(cause);
53     }
54 
ZygoteStartFailedEx(String s, Throwable cause)55     ZygoteStartFailedEx(String s, Throwable cause) {
56         super(s, cause);
57     }
58 }
59 
60 /**
61  * Maintains communication state with the zygote processes. This class is responsible
62  * for the sockets opened to the zygotes and for starting processes on behalf of the
63  * {@link android.os.Process} class.
64  *
65  * {@hide}
66  */
67 public class ZygoteProcess {
68 
69     private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000;
70 
71     /**
72      * Use a relatively short delay, because for app zygote, this is in the critical path of
73      * service launch.
74      */
75     private static final int ZYGOTE_CONNECT_RETRY_DELAY_MS = 50;
76 
77     private static final String LOG_TAG = "ZygoteProcess";
78 
79     /**
80      * The default value for enabling the unspecialized app process (USAP) pool.  This value will
81      * not be used if the devices has a DeviceConfig profile pushed to it that contains a value for
82      * this key.
83      */
84     private static final String USAP_POOL_ENABLED_DEFAULT = "false";
85 
86     /**
87      * The name of the socket used to communicate with the primary zygote.
88      */
89     private final LocalSocketAddress mZygoteSocketAddress;
90 
91     /**
92      * The name of the secondary (alternate ABI) zygote socket.
93      */
94     private final LocalSocketAddress mZygoteSecondarySocketAddress;
95 
96     /**
97      * The name of the socket used to communicate with the primary USAP pool.
98      */
99     private final LocalSocketAddress mUsapPoolSocketAddress;
100 
101     /**
102      * The name of the socket used to communicate with the secondary (alternate ABI) USAP pool.
103      */
104     private final LocalSocketAddress mUsapPoolSecondarySocketAddress;
105 
ZygoteProcess()106     public ZygoteProcess() {
107         mZygoteSocketAddress =
108                 new LocalSocketAddress(Zygote.PRIMARY_SOCKET_NAME,
109                                        LocalSocketAddress.Namespace.RESERVED);
110         mZygoteSecondarySocketAddress =
111                 new LocalSocketAddress(Zygote.SECONDARY_SOCKET_NAME,
112                                        LocalSocketAddress.Namespace.RESERVED);
113 
114         mUsapPoolSocketAddress =
115                 new LocalSocketAddress(Zygote.USAP_POOL_PRIMARY_SOCKET_NAME,
116                                        LocalSocketAddress.Namespace.RESERVED);
117         mUsapPoolSecondarySocketAddress =
118                 new LocalSocketAddress(Zygote.USAP_POOL_SECONDARY_SOCKET_NAME,
119                                        LocalSocketAddress.Namespace.RESERVED);
120     }
121 
ZygoteProcess(LocalSocketAddress primarySocketAddress, LocalSocketAddress secondarySocketAddress)122     public ZygoteProcess(LocalSocketAddress primarySocketAddress,
123                          LocalSocketAddress secondarySocketAddress) {
124         mZygoteSocketAddress = primarySocketAddress;
125         mZygoteSecondarySocketAddress = secondarySocketAddress;
126 
127         mUsapPoolSocketAddress = null;
128         mUsapPoolSecondarySocketAddress = null;
129     }
130 
getPrimarySocketAddress()131     public LocalSocketAddress getPrimarySocketAddress() {
132         return mZygoteSocketAddress;
133     }
134 
135     /**
136      * State for communicating with the zygote process.
137      */
138     private static class ZygoteState implements AutoCloseable {
139         final LocalSocketAddress mZygoteSocketAddress;
140         final LocalSocketAddress mUsapSocketAddress;
141 
142         private final LocalSocket mZygoteSessionSocket;
143 
144         final DataInputStream mZygoteInputStream;
145         final BufferedWriter mZygoteOutputWriter;
146 
147         private final List<String> mAbiList;
148 
149         private boolean mClosed;
150 
ZygoteState(LocalSocketAddress zygoteSocketAddress, LocalSocketAddress usapSocketAddress, LocalSocket zygoteSessionSocket, DataInputStream zygoteInputStream, BufferedWriter zygoteOutputWriter, List<String> abiList)151         private ZygoteState(LocalSocketAddress zygoteSocketAddress,
152                             LocalSocketAddress usapSocketAddress,
153                             LocalSocket zygoteSessionSocket,
154                             DataInputStream zygoteInputStream,
155                             BufferedWriter zygoteOutputWriter,
156                             List<String> abiList) {
157             this.mZygoteSocketAddress = zygoteSocketAddress;
158             this.mUsapSocketAddress = usapSocketAddress;
159             this.mZygoteSessionSocket = zygoteSessionSocket;
160             this.mZygoteInputStream = zygoteInputStream;
161             this.mZygoteOutputWriter = zygoteOutputWriter;
162             this.mAbiList = abiList;
163         }
164 
165         /**
166          * Create a new ZygoteState object by connecting to the given Zygote socket and saving the
167          * given USAP socket address.
168          *
169          * @param zygoteSocketAddress  Zygote socket to connect to
170          * @param usapSocketAddress  USAP socket address to save for later
171          * @return  A new ZygoteState object containing a session socket for the given Zygote socket
172          * address
173          * @throws IOException
174          */
connect(@onNull LocalSocketAddress zygoteSocketAddress, @Nullable LocalSocketAddress usapSocketAddress)175         static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,
176                 @Nullable LocalSocketAddress usapSocketAddress)
177                 throws IOException {
178 
179             DataInputStream zygoteInputStream;
180             BufferedWriter zygoteOutputWriter;
181             final LocalSocket zygoteSessionSocket = new LocalSocket();
182 
183             if (zygoteSocketAddress == null) {
184                 throw new IllegalArgumentException("zygoteSocketAddress can't be null");
185             }
186 
187             try {
188                 zygoteSessionSocket.connect(zygoteSocketAddress);
189                 zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
190                 zygoteOutputWriter =
191                         new BufferedWriter(
192                                 new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
193                                 Zygote.SOCKET_BUFFER_SIZE);
194             } catch (IOException ex) {
195                 try {
196                     zygoteSessionSocket.close();
197                 } catch (IOException ignore) { }
198 
199                 throw ex;
200             }
201 
202             return new ZygoteState(zygoteSocketAddress, usapSocketAddress,
203                                    zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
204                                    getAbiList(zygoteOutputWriter, zygoteInputStream));
205         }
206 
getUsapSessionSocket()207         LocalSocket getUsapSessionSocket() throws IOException {
208             final LocalSocket usapSessionSocket = new LocalSocket();
209             usapSessionSocket.connect(this.mUsapSocketAddress);
210 
211             return usapSessionSocket;
212         }
213 
matches(String abi)214         boolean matches(String abi) {
215             return mAbiList.contains(abi);
216         }
217 
close()218         public void close() {
219             try {
220                 mZygoteSessionSocket.close();
221             } catch (IOException ex) {
222                 Log.e(LOG_TAG,"I/O exception on routine close", ex);
223             }
224 
225             mClosed = true;
226         }
227 
isClosed()228         boolean isClosed() {
229             return mClosed;
230         }
231     }
232 
233     /**
234      * Lock object to protect access to the two ZygoteStates below. This lock must be
235      * acquired while communicating over the ZygoteState's socket, to prevent
236      * interleaved access.
237      */
238     private final Object mLock = new Object();
239 
240     /**
241      * List of exemptions to the API blacklist. These are prefix matches on the runtime format
242      * symbol signature. Any matching symbol is treated by the runtime as being on the light grey
243      * list.
244      */
245     private List<String> mApiBlacklistExemptions = Collections.emptyList();
246 
247     /**
248      * Proportion of hidden API accesses that should be logged to the event log; 0 - 0x10000.
249      */
250     private int mHiddenApiAccessLogSampleRate;
251 
252     /**
253      * Proportion of hidden API accesses that should be logged to statslog; 0 - 0x10000.
254      */
255     private int mHiddenApiAccessStatslogSampleRate;
256 
257     /**
258      * The state of the connection to the primary zygote.
259      */
260     private ZygoteState primaryZygoteState;
261 
262     /**
263      * The state of the connection to the secondary zygote.
264      */
265     private ZygoteState secondaryZygoteState;
266 
267     /**
268      * If the USAP pool should be created and used to start applications.
269      *
270      * Setting this value to false will disable the creation, maintenance, and use of the USAP
271      * pool.  When the USAP pool is disabled the application lifecycle will be identical to
272      * previous versions of Android.
273      */
274     private boolean mUsapPoolEnabled = false;
275 
276     /**
277      * Start a new process.
278      *
279      * <p>If processes are enabled, a new process is created and the
280      * static main() function of a <var>processClass</var> is executed there.
281      * The process will continue running after this function returns.
282      *
283      * <p>If processes are not enabled, a new thread in the caller's
284      * process is created and main() of <var>processclass</var> called there.
285      *
286      * <p>The niceName parameter, if not an empty string, is a custom name to
287      * give to the process instead of using processClass.  This allows you to
288      * make easily identifyable processes even if you are using the same base
289      * <var>processClass</var> to start them.
290      *
291      * When invokeWith is not null, the process will be started as a fresh app
292      * and not a zygote fork. Note that this is only allowed for uid 0 or when
293      * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
294      *
295      * @param processClass The class to use as the process's main entry
296      *                     point.
297      * @param niceName A more readable name to use for the process.
298      * @param uid The user-id under which the process will run.
299      * @param gid The group-id under which the process will run.
300      * @param gids Additional group-ids associated with the process.
301      * @param runtimeFlags Additional flags.
302      * @param targetSdkVersion The target SDK version for the app.
303      * @param seInfo null-ok SELinux information for the new process.
304      * @param abi non-null the ABI this app should be started with.
305      * @param instructionSet null-ok the instruction set to use.
306      * @param appDataDir null-ok the data directory of the app.
307      * @param invokeWith null-ok the command to invoke with.
308      * @param packageName null-ok the name of the package this process belongs to.
309      * @param zygoteArgs Additional arguments to supply to the zygote process.
310      *
311      * @return An object that describes the result of the attempt to start the process.
312      * @throws RuntimeException on fatal start failure
313      */
start(@onNull final String processClass, final String niceName, int uid, int gid, @Nullable int[] gids, int runtimeFlags, int mountExternal, int targetSdkVersion, @Nullable String seInfo, @NonNull String abi, @Nullable String instructionSet, @Nullable String appDataDir, @Nullable String invokeWith, @Nullable String packageName, boolean useUsapPool, @Nullable String[] zygoteArgs)314     public final Process.ProcessStartResult start(@NonNull final String processClass,
315                                                   final String niceName,
316                                                   int uid, int gid, @Nullable int[] gids,
317                                                   int runtimeFlags, int mountExternal,
318                                                   int targetSdkVersion,
319                                                   @Nullable String seInfo,
320                                                   @NonNull String abi,
321                                                   @Nullable String instructionSet,
322                                                   @Nullable String appDataDir,
323                                                   @Nullable String invokeWith,
324                                                   @Nullable String packageName,
325                                                   boolean useUsapPool,
326                                                   @Nullable String[] zygoteArgs) {
327         // TODO (chriswailes): Is there a better place to check this value?
328         if (fetchUsapPoolEnabledPropWithMinInterval()) {
329             informZygotesOfUsapPoolStatus();
330         }
331 
332         try {
333             return startViaZygote(processClass, niceName, uid, gid, gids,
334                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
335                     abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
336                     packageName, useUsapPool, zygoteArgs);
337         } catch (ZygoteStartFailedEx ex) {
338             Log.e(LOG_TAG,
339                     "Starting VM process through Zygote failed");
340             throw new RuntimeException(
341                     "Starting VM process through Zygote failed", ex);
342         }
343     }
344 
345     /** retry interval for opening a zygote socket */
346     static final int ZYGOTE_RETRY_MILLIS = 500;
347 
348     /**
349      * Queries the zygote for the list of ABIS it supports.
350      */
351     @GuardedBy("mLock")
getAbiList(BufferedWriter writer, DataInputStream inputStream)352     private static List<String> getAbiList(BufferedWriter writer, DataInputStream inputStream)
353             throws IOException {
354         // Each query starts with the argument count (1 in this case)
355         writer.write("1");
356         // ... followed by a new-line.
357         writer.newLine();
358         // ... followed by our only argument.
359         writer.write("--query-abi-list");
360         writer.newLine();
361         writer.flush();
362 
363         // The response is a length prefixed stream of ASCII bytes.
364         int numBytes = inputStream.readInt();
365         byte[] bytes = new byte[numBytes];
366         inputStream.readFully(bytes);
367 
368         final String rawList = new String(bytes, StandardCharsets.US_ASCII);
369 
370         return Arrays.asList(rawList.split(","));
371     }
372 
373     /**
374      * Sends an argument list to the zygote process, which starts a new child
375      * and returns the child's pid. Please note: the present implementation
376      * replaces newlines in the argument list with spaces.
377      *
378      * @throws ZygoteStartFailedEx if process start failed for any reason
379      */
380     @GuardedBy("mLock")
zygoteSendArgsAndGetResult( ZygoteState zygoteState, boolean useUsapPool, @NonNull ArrayList<String> args)381     private Process.ProcessStartResult zygoteSendArgsAndGetResult(
382             ZygoteState zygoteState, boolean useUsapPool, @NonNull ArrayList<String> args)
383             throws ZygoteStartFailedEx {
384         // Throw early if any of the arguments are malformed. This means we can
385         // avoid writing a partial response to the zygote.
386         for (String arg : args) {
387             // Making two indexOf calls here is faster than running a manually fused loop due
388             // to the fact that indexOf is a optimized intrinsic.
389             if (arg.indexOf('\n') >= 0) {
390                 throw new ZygoteStartFailedEx("Embedded newlines not allowed");
391             } else if (arg.indexOf('\r') >= 0) {
392                 throw new ZygoteStartFailedEx("Embedded carriage returns not allowed");
393             }
394         }
395 
396         /*
397          * See com.android.internal.os.ZygoteArguments.parseArgs()
398          * Presently the wire format to the zygote process is:
399          * a) a count of arguments (argc, in essence)
400          * b) a number of newline-separated argument strings equal to count
401          *
402          * After the zygote process reads these it will write the pid of
403          * the child or -1 on failure, followed by boolean to
404          * indicate whether a wrapper process was used.
405          */
406         String msgStr = args.size() + "\n" + String.join("\n", args) + "\n";
407 
408         if (useUsapPool && mUsapPoolEnabled && canAttemptUsap(args)) {
409             try {
410                 return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);
411             } catch (IOException ex) {
412                 // If there was an IOException using the USAP pool we will log the error and
413                 // attempt to start the process through the Zygote.
414                 Log.e(LOG_TAG, "IO Exception while communicating with USAP pool - "
415                         + ex.getMessage());
416             }
417         }
418 
419         return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
420     }
421 
attemptZygoteSendArgsAndGetResult( ZygoteState zygoteState, String msgStr)422     private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
423             ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
424         try {
425             final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
426             final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;
427 
428             zygoteWriter.write(msgStr);
429             zygoteWriter.flush();
430 
431             // Always read the entire result from the input stream to avoid leaving
432             // bytes in the stream for future process starts to accidentally stumble
433             // upon.
434             Process.ProcessStartResult result = new Process.ProcessStartResult();
435             result.pid = zygoteInputStream.readInt();
436             result.usingWrapper = zygoteInputStream.readBoolean();
437 
438             if (result.pid < 0) {
439                 throw new ZygoteStartFailedEx("fork() failed");
440             }
441 
442             return result;
443         } catch (IOException ex) {
444             zygoteState.close();
445             Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
446                     + ex.toString());
447             throw new ZygoteStartFailedEx(ex);
448         }
449     }
450 
attemptUsapSendArgsAndGetResult( ZygoteState zygoteState, String msgStr)451     private Process.ProcessStartResult attemptUsapSendArgsAndGetResult(
452             ZygoteState zygoteState, String msgStr)
453             throws ZygoteStartFailedEx, IOException {
454         try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) {
455             final BufferedWriter usapWriter =
456                     new BufferedWriter(
457                             new OutputStreamWriter(usapSessionSocket.getOutputStream()),
458                             Zygote.SOCKET_BUFFER_SIZE);
459             final DataInputStream usapReader =
460                     new DataInputStream(usapSessionSocket.getInputStream());
461 
462             usapWriter.write(msgStr);
463             usapWriter.flush();
464 
465             Process.ProcessStartResult result = new Process.ProcessStartResult();
466             result.pid = usapReader.readInt();
467             // USAPs can't be used to spawn processes that need wrappers.
468             result.usingWrapper = false;
469 
470             if (result.pid >= 0) {
471                 return result;
472             } else {
473                 throw new ZygoteStartFailedEx("USAP specialization failed");
474             }
475         }
476     }
477 
478     /**
479      * Flags that may not be passed to a USAP.
480      */
481     private static final String[] INVALID_USAP_FLAGS = {
482         "--query-abi-list",
483         "--get-pid",
484         "--preload-default",
485         "--preload-package",
486         "--preload-app",
487         "--start-child-zygote",
488         "--set-api-blacklist-exemptions",
489         "--hidden-api-log-sampling-rate",
490         "--hidden-api-statslog-sampling-rate",
491         "--invoke-with"
492     };
493 
494     /**
495      * Tests a command list to see if it is valid to send to a USAP.
496      * @param args  Zygote/USAP command arguments
497      * @return  True if the command can be passed to a USAP; false otherwise
498      */
canAttemptUsap(ArrayList<String> args)499     private static boolean canAttemptUsap(ArrayList<String> args) {
500         for (String flag : args) {
501             for (String badFlag : INVALID_USAP_FLAGS) {
502                 if (flag.startsWith(badFlag)) {
503                     return false;
504                 }
505             }
506             if (flag.startsWith("--nice-name=")) {
507                 // Check if the wrap property is set, usap would ignore it.
508                 String niceName = flag.substring(12);
509                 String property_value = SystemProperties.get("wrap." + niceName);
510                 if (property_value != null && property_value.length() != 0) {
511                     return false;
512                 }
513             }
514         }
515 
516         return true;
517     }
518 
519     /**
520      * Starts a new process via the zygote mechanism.
521      *
522      * @param processClass Class name whose static main() to run
523      * @param niceName 'nice' process name to appear in ps
524      * @param uid a POSIX uid that the new process should setuid() to
525      * @param gid a POSIX gid that the new process shuold setgid() to
526      * @param gids null-ok; a list of supplementary group IDs that the
527      * new process should setgroup() to.
528      * @param runtimeFlags Additional flags for the runtime.
529      * @param targetSdkVersion The target SDK version for the app.
530      * @param seInfo null-ok SELinux information for the new process.
531      * @param abi the ABI the process should use.
532      * @param instructionSet null-ok the instruction set to use.
533      * @param appDataDir null-ok the data directory of the app.
534      * @param startChildZygote Start a sub-zygote. This creates a new zygote process
535      * that has its state cloned from this zygote process.
536      * @param packageName null-ok the name of the package this process belongs to.
537      * @param extraArgs Additional arguments to supply to the zygote process.
538      * @return An object that describes the result of the attempt to start the process.
539      * @throws ZygoteStartFailedEx if process start failed for any reason
540      */
startViaZygote(@onNull final String processClass, @Nullable final String niceName, final int uid, final int gid, @Nullable final int[] gids, int runtimeFlags, int mountExternal, int targetSdkVersion, @Nullable String seInfo, @NonNull String abi, @Nullable String instructionSet, @Nullable String appDataDir, @Nullable String invokeWith, boolean startChildZygote, @Nullable String packageName, boolean useUsapPool, @Nullable String[] extraArgs)541     private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
542                                                       @Nullable final String niceName,
543                                                       final int uid, final int gid,
544                                                       @Nullable final int[] gids,
545                                                       int runtimeFlags, int mountExternal,
546                                                       int targetSdkVersion,
547                                                       @Nullable String seInfo,
548                                                       @NonNull String abi,
549                                                       @Nullable String instructionSet,
550                                                       @Nullable String appDataDir,
551                                                       @Nullable String invokeWith,
552                                                       boolean startChildZygote,
553                                                       @Nullable String packageName,
554                                                       boolean useUsapPool,
555                                                       @Nullable String[] extraArgs)
556                                                       throws ZygoteStartFailedEx {
557         ArrayList<String> argsForZygote = new ArrayList<>();
558 
559         // --runtime-args, --setuid=, --setgid=,
560         // and --setgroups= must go first
561         argsForZygote.add("--runtime-args");
562         argsForZygote.add("--setuid=" + uid);
563         argsForZygote.add("--setgid=" + gid);
564         argsForZygote.add("--runtime-flags=" + runtimeFlags);
565         if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
566             argsForZygote.add("--mount-external-default");
567         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
568             argsForZygote.add("--mount-external-read");
569         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
570             argsForZygote.add("--mount-external-write");
571         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) {
572             argsForZygote.add("--mount-external-full");
573         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
574             argsForZygote.add("--mount-external-installer");
575         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_LEGACY) {
576             argsForZygote.add("--mount-external-legacy");
577         }
578 
579         argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
580 
581         // --setgroups is a comma-separated list
582         if (gids != null && gids.length > 0) {
583             StringBuilder sb = new StringBuilder();
584             sb.append("--setgroups=");
585 
586             int sz = gids.length;
587             for (int i = 0; i < sz; i++) {
588                 if (i != 0) {
589                     sb.append(',');
590                 }
591                 sb.append(gids[i]);
592             }
593 
594             argsForZygote.add(sb.toString());
595         }
596 
597         if (niceName != null) {
598             argsForZygote.add("--nice-name=" + niceName);
599         }
600 
601         if (seInfo != null) {
602             argsForZygote.add("--seinfo=" + seInfo);
603         }
604 
605         if (instructionSet != null) {
606             argsForZygote.add("--instruction-set=" + instructionSet);
607         }
608 
609         if (appDataDir != null) {
610             argsForZygote.add("--app-data-dir=" + appDataDir);
611         }
612 
613         if (invokeWith != null) {
614             argsForZygote.add("--invoke-with");
615             argsForZygote.add(invokeWith);
616         }
617 
618         if (startChildZygote) {
619             argsForZygote.add("--start-child-zygote");
620         }
621 
622         if (packageName != null) {
623             argsForZygote.add("--package-name=" + packageName);
624         }
625 
626         argsForZygote.add(processClass);
627 
628         if (extraArgs != null) {
629             Collections.addAll(argsForZygote, extraArgs);
630         }
631 
632         synchronized(mLock) {
633             // The USAP pool can not be used if the application will not use the systems graphics
634             // driver.  If that driver is requested use the Zygote application start path.
635             return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
636                                               useUsapPool,
637                                               argsForZygote);
638         }
639     }
640 
fetchUsapPoolEnabledProp()641     private boolean fetchUsapPoolEnabledProp() {
642         boolean origVal = mUsapPoolEnabled;
643 
644         final String propertyString = Zygote.getConfigurationProperty(
645                 ZygoteConfig.USAP_POOL_ENABLED, USAP_POOL_ENABLED_DEFAULT);
646 
647         if (!propertyString.isEmpty()) {
648             mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean(
649                   ZygoteConfig.USAP_POOL_ENABLED,
650                   Boolean.parseBoolean(USAP_POOL_ENABLED_DEFAULT));
651         }
652 
653         boolean valueChanged = origVal != mUsapPoolEnabled;
654 
655         if (valueChanged) {
656             Log.i(LOG_TAG, "usapPoolEnabled = " + mUsapPoolEnabled);
657         }
658 
659         return valueChanged;
660     }
661 
662     private boolean mIsFirstPropCheck = true;
663     private long mLastPropCheckTimestamp = 0;
664 
fetchUsapPoolEnabledPropWithMinInterval()665     private boolean fetchUsapPoolEnabledPropWithMinInterval() {
666         final long currentTimestamp = SystemClock.elapsedRealtime();
667 
668         if (SystemProperties.get("dalvik.vm.boot-image", "").endsWith("apex.art")) {
669             // TODO(b/119800099): In jitzygote mode, we want to start using USAP processes
670             // only once the boot classpath has been compiled. There is currently no callback
671             // from the runtime to notify the zygote about end of compilation, so for now just
672             // arbitrarily start USAP processes 15 seconds after boot.
673             if (currentTimestamp <= 15000) {
674                 return false;
675             }
676         }
677 
678         if (mIsFirstPropCheck
679                 || (currentTimestamp - mLastPropCheckTimestamp >= Zygote.PROPERTY_CHECK_INTERVAL)) {
680             mIsFirstPropCheck = false;
681             mLastPropCheckTimestamp = currentTimestamp;
682             return fetchUsapPoolEnabledProp();
683         }
684 
685         return false;
686     }
687 
688     /**
689      * Closes the connections to the zygote, if they exist.
690      */
close()691     public void close() {
692         if (primaryZygoteState != null) {
693             primaryZygoteState.close();
694         }
695         if (secondaryZygoteState != null) {
696             secondaryZygoteState.close();
697         }
698     }
699 
700     /**
701      * Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
702      * and retry if the zygote is unresponsive. This method is a no-op if a connection is
703      * already open.
704      */
establishZygoteConnectionForAbi(String abi)705     public void establishZygoteConnectionForAbi(String abi) {
706         try {
707             synchronized(mLock) {
708                 openZygoteSocketIfNeeded(abi);
709             }
710         } catch (ZygoteStartFailedEx ex) {
711             throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
712         }
713     }
714 
715     /**
716      * Attempt to retrieve the PID of the zygote serving the given abi.
717      */
getZygotePid(String abi)718     public int getZygotePid(String abi) {
719         try {
720             synchronized (mLock) {
721                 ZygoteState state = openZygoteSocketIfNeeded(abi);
722 
723                 // Each query starts with the argument count (1 in this case)
724                 state.mZygoteOutputWriter.write("1");
725                 // ... followed by a new-line.
726                 state.mZygoteOutputWriter.newLine();
727                 // ... followed by our only argument.
728                 state.mZygoteOutputWriter.write("--get-pid");
729                 state.mZygoteOutputWriter.newLine();
730                 state.mZygoteOutputWriter.flush();
731 
732                 // The response is a length prefixed stream of ASCII bytes.
733                 int numBytes = state.mZygoteInputStream.readInt();
734                 byte[] bytes = new byte[numBytes];
735                 state.mZygoteInputStream.readFully(bytes);
736 
737                 return Integer.parseInt(new String(bytes, StandardCharsets.US_ASCII));
738             }
739         } catch (Exception ex) {
740             throw new RuntimeException("Failure retrieving pid", ex);
741         }
742     }
743 
744     /**
745      * Push hidden API blacklisting exemptions into the zygote process(es).
746      *
747      * <p>The list of exemptions will take affect for all new processes forked from the zygote after
748      * this call.
749      *
750      * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as
751      *        whitelisted/public APIs (i.e. allowed, no logging of usage).
752      */
setApiBlacklistExemptions(List<String> exemptions)753     public boolean setApiBlacklistExemptions(List<String> exemptions) {
754         synchronized (mLock) {
755             mApiBlacklistExemptions = exemptions;
756             boolean ok = maybeSetApiBlacklistExemptions(primaryZygoteState, true);
757             if (ok) {
758                 ok = maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
759             }
760             return ok;
761         }
762     }
763 
764     /**
765      * Set the precentage of detected hidden API accesses that are logged to the event log.
766      *
767      * <p>This rate will take affect for all new processes forked from the zygote after this call.
768      *
769      * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging.
770      */
setHiddenApiAccessLogSampleRate(int rate)771     public void setHiddenApiAccessLogSampleRate(int rate) {
772         synchronized (mLock) {
773             mHiddenApiAccessLogSampleRate = rate;
774             maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
775             maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
776         }
777     }
778 
779     /**
780      * Set the precentage of detected hidden API accesses that are logged to the new event log.
781      *
782      * <p>This rate will take affect for all new processes forked from the zygote after this call.
783      *
784      * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging.
785      */
setHiddenApiAccessStatslogSampleRate(int rate)786     public void setHiddenApiAccessStatslogSampleRate(int rate) {
787         synchronized (mLock) {
788             mHiddenApiAccessStatslogSampleRate = rate;
789             maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState);
790             maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState);
791         }
792     }
793 
794     @GuardedBy("mLock")
maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty)795     private boolean maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
796         if (state == null || state.isClosed()) {
797             Slog.e(LOG_TAG, "Can't set API blacklist exemptions: no zygote connection");
798             return false;
799         } else if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
800             return true;
801         }
802 
803         try {
804             state.mZygoteOutputWriter.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
805             state.mZygoteOutputWriter.newLine();
806             state.mZygoteOutputWriter.write("--set-api-blacklist-exemptions");
807             state.mZygoteOutputWriter.newLine();
808             for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
809                 state.mZygoteOutputWriter.write(mApiBlacklistExemptions.get(i));
810                 state.mZygoteOutputWriter.newLine();
811             }
812             state.mZygoteOutputWriter.flush();
813             int status = state.mZygoteInputStream.readInt();
814             if (status != 0) {
815                 Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
816             }
817             return true;
818         } catch (IOException ioe) {
819             Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
820             mApiBlacklistExemptions = Collections.emptyList();
821             return false;
822         }
823     }
824 
maybeSetHiddenApiAccessLogSampleRate(ZygoteState state)825     private void maybeSetHiddenApiAccessLogSampleRate(ZygoteState state) {
826         if (state == null || state.isClosed() || mHiddenApiAccessLogSampleRate == -1) {
827             return;
828         }
829 
830         try {
831             state.mZygoteOutputWriter.write(Integer.toString(1));
832             state.mZygoteOutputWriter.newLine();
833             state.mZygoteOutputWriter.write("--hidden-api-log-sampling-rate="
834                     + mHiddenApiAccessLogSampleRate);
835             state.mZygoteOutputWriter.newLine();
836             state.mZygoteOutputWriter.flush();
837             int status = state.mZygoteInputStream.readInt();
838             if (status != 0) {
839                 Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status);
840             }
841         } catch (IOException ioe) {
842             Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate", ioe);
843         }
844     }
845 
maybeSetHiddenApiAccessStatslogSampleRate(ZygoteState state)846     private void maybeSetHiddenApiAccessStatslogSampleRate(ZygoteState state) {
847         if (state == null || state.isClosed() || mHiddenApiAccessStatslogSampleRate == -1) {
848             return;
849         }
850 
851         try {
852             state.mZygoteOutputWriter.write(Integer.toString(1));
853             state.mZygoteOutputWriter.newLine();
854             state.mZygoteOutputWriter.write("--hidden-api-statslog-sampling-rate="
855                     + mHiddenApiAccessStatslogSampleRate);
856             state.mZygoteOutputWriter.newLine();
857             state.mZygoteOutputWriter.flush();
858             int status = state.mZygoteInputStream.readInt();
859             if (status != 0) {
860                 Slog.e(LOG_TAG, "Failed to set hidden API statslog sampling rate; status "
861                         + status);
862             }
863         } catch (IOException ioe) {
864             Slog.e(LOG_TAG, "Failed to set hidden API statslog sampling rate", ioe);
865         }
866     }
867 
868     /**
869      * Creates a ZygoteState for the primary zygote if it doesn't exist or has been disconnected.
870      */
871     @GuardedBy("mLock")
attemptConnectionToPrimaryZygote()872     private void attemptConnectionToPrimaryZygote() throws IOException {
873         if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
874             primaryZygoteState =
875                     ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);
876 
877             maybeSetApiBlacklistExemptions(primaryZygoteState, false);
878             maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
879             maybeSetHiddenApiAccessStatslogSampleRate(primaryZygoteState);
880         }
881     }
882 
883     /**
884      * Creates a ZygoteState for the secondary zygote if it doesn't exist or has been disconnected.
885      */
886     @GuardedBy("mLock")
attemptConnectionToSecondaryZygote()887     private void attemptConnectionToSecondaryZygote() throws IOException {
888         if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
889             secondaryZygoteState =
890                     ZygoteState.connect(mZygoteSecondarySocketAddress,
891                             mUsapPoolSecondarySocketAddress);
892 
893             maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
894             maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
895             maybeSetHiddenApiAccessStatslogSampleRate(secondaryZygoteState);
896         }
897     }
898 
899     /**
900      * Tries to open a session socket to a Zygote process with a compatible ABI if one is not
901      * already open. If a compatible session socket is already open that session socket is returned.
902      * This function may block and may have to try connecting to multiple Zygotes to find the
903      * appropriate one.  Requires that mLock be held.
904      */
905     @GuardedBy("mLock")
openZygoteSocketIfNeeded(String abi)906     private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
907         try {
908             attemptConnectionToPrimaryZygote();
909 
910             if (primaryZygoteState.matches(abi)) {
911                 return primaryZygoteState;
912             }
913 
914             if (mZygoteSecondarySocketAddress != null) {
915                 // The primary zygote didn't match. Try the secondary.
916                 attemptConnectionToSecondaryZygote();
917 
918                 if (secondaryZygoteState.matches(abi)) {
919                     return secondaryZygoteState;
920                 }
921             }
922         } catch (IOException ioe) {
923             throw new ZygoteStartFailedEx("Error connecting to zygote", ioe);
924         }
925 
926         throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
927     }
928 
929     /**
930      * Instructs the zygote to pre-load the application code for the given Application.
931      * Only the app zygote supports this function.
932      * TODO preloadPackageForAbi() can probably be removed and the callers an use this instead.
933      */
preloadApp(ApplicationInfo appInfo, String abi)934     public boolean preloadApp(ApplicationInfo appInfo, String abi)
935             throws ZygoteStartFailedEx, IOException {
936         synchronized (mLock) {
937             ZygoteState state = openZygoteSocketIfNeeded(abi);
938             state.mZygoteOutputWriter.write("2");
939             state.mZygoteOutputWriter.newLine();
940 
941             state.mZygoteOutputWriter.write("--preload-app");
942             state.mZygoteOutputWriter.newLine();
943 
944             // Zygote args needs to be strings, so in order to pass ApplicationInfo,
945             // write it to a Parcel, and base64 the raw Parcel bytes to the other side.
946             Parcel parcel = Parcel.obtain();
947             appInfo.writeToParcel(parcel, 0 /* flags */);
948             String encodedParcelData = Base64.getEncoder().encodeToString(parcel.marshall());
949             parcel.recycle();
950             state.mZygoteOutputWriter.write(encodedParcelData);
951             state.mZygoteOutputWriter.newLine();
952 
953             state.mZygoteOutputWriter.flush();
954 
955             return (state.mZygoteInputStream.readInt() == 0);
956         }
957     }
958 
959     /**
960      * Instructs the zygote to pre-load the classes and native libraries at the given paths
961      * for the specified abi. Not all zygotes support this function.
962      */
preloadPackageForAbi( String packagePath, String libsPath, String libFileName, String cacheKey, String abi)963     public boolean preloadPackageForAbi(
964             String packagePath, String libsPath, String libFileName, String cacheKey, String abi)
965             throws ZygoteStartFailedEx, IOException {
966         synchronized (mLock) {
967             ZygoteState state = openZygoteSocketIfNeeded(abi);
968             state.mZygoteOutputWriter.write("5");
969             state.mZygoteOutputWriter.newLine();
970 
971             state.mZygoteOutputWriter.write("--preload-package");
972             state.mZygoteOutputWriter.newLine();
973 
974             state.mZygoteOutputWriter.write(packagePath);
975             state.mZygoteOutputWriter.newLine();
976 
977             state.mZygoteOutputWriter.write(libsPath);
978             state.mZygoteOutputWriter.newLine();
979 
980             state.mZygoteOutputWriter.write(libFileName);
981             state.mZygoteOutputWriter.newLine();
982 
983             state.mZygoteOutputWriter.write(cacheKey);
984             state.mZygoteOutputWriter.newLine();
985 
986             state.mZygoteOutputWriter.flush();
987 
988             return (state.mZygoteInputStream.readInt() == 0);
989         }
990     }
991 
992     /**
993      * Instructs the zygote to preload the default set of classes and resources. Returns
994      * {@code true} if a preload was performed as a result of this call, and {@code false}
995      * otherwise. The latter usually means that the zygote eagerly preloaded at startup
996      * or due to a previous call to {@code preloadDefault}. Note that this call is synchronous.
997      */
preloadDefault(String abi)998     public boolean preloadDefault(String abi) throws ZygoteStartFailedEx, IOException {
999         synchronized (mLock) {
1000             ZygoteState state = openZygoteSocketIfNeeded(abi);
1001             // Each query starts with the argument count (1 in this case)
1002             state.mZygoteOutputWriter.write("1");
1003             state.mZygoteOutputWriter.newLine();
1004             state.mZygoteOutputWriter.write("--preload-default");
1005             state.mZygoteOutputWriter.newLine();
1006             state.mZygoteOutputWriter.flush();
1007 
1008             return (state.mZygoteInputStream.readInt() == 0);
1009         }
1010     }
1011 
1012     /**
1013      * Try connecting to the Zygote over and over again until we hit a time-out.
1014      * @param zygoteSocketName The name of the socket to connect to.
1015      */
waitForConnectionToZygote(String zygoteSocketName)1016     public static void waitForConnectionToZygote(String zygoteSocketName) {
1017         final LocalSocketAddress zygoteSocketAddress =
1018                 new LocalSocketAddress(zygoteSocketName, LocalSocketAddress.Namespace.RESERVED);
1019         waitForConnectionToZygote(zygoteSocketAddress);
1020     }
1021 
1022     /**
1023      * Try connecting to the Zygote over and over again until we hit a time-out.
1024      * @param zygoteSocketAddress The name of the socket to connect to.
1025      */
waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress)1026     public static void waitForConnectionToZygote(LocalSocketAddress zygoteSocketAddress) {
1027         int numRetries = ZYGOTE_CONNECT_TIMEOUT_MS / ZYGOTE_CONNECT_RETRY_DELAY_MS;
1028         for (int n = numRetries; n >= 0; n--) {
1029             try {
1030                 final ZygoteState zs =
1031                         ZygoteState.connect(zygoteSocketAddress, null);
1032                 zs.close();
1033                 return;
1034             } catch (IOException ioe) {
1035                 Log.w(LOG_TAG,
1036                         "Got error connecting to zygote, retrying. msg= " + ioe.getMessage());
1037             }
1038 
1039             try {
1040                 Thread.sleep(ZYGOTE_CONNECT_RETRY_DELAY_MS);
1041             } catch (InterruptedException ignored) { }
1042         }
1043         Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket "
1044                 + zygoteSocketAddress.getName());
1045     }
1046 
1047     /**
1048      * Sends messages to the zygotes telling them to change the status of their USAP pools.  If
1049      * this notification fails the ZygoteProcess will fall back to the previous behavior.
1050      */
informZygotesOfUsapPoolStatus()1051     private void informZygotesOfUsapPoolStatus() {
1052         final String command = "1\n--usap-pool-enabled=" + mUsapPoolEnabled + "\n";
1053 
1054         synchronized (mLock) {
1055             try {
1056                 attemptConnectionToPrimaryZygote();
1057 
1058                 primaryZygoteState.mZygoteOutputWriter.write(command);
1059                 primaryZygoteState.mZygoteOutputWriter.flush();
1060             } catch (IOException ioe) {
1061                 mUsapPoolEnabled = !mUsapPoolEnabled;
1062                 Log.w(LOG_TAG, "Failed to inform zygotes of USAP pool status: "
1063                         + ioe.getMessage());
1064                 return;
1065             }
1066 
1067             if (mZygoteSecondarySocketAddress != null) {
1068                 try {
1069                     attemptConnectionToSecondaryZygote();
1070 
1071                     try {
1072                         secondaryZygoteState.mZygoteOutputWriter.write(command);
1073                         secondaryZygoteState.mZygoteOutputWriter.flush();
1074 
1075                         // Wait for the secondary Zygote to finish its work.
1076                         secondaryZygoteState.mZygoteInputStream.readInt();
1077                     } catch (IOException ioe) {
1078                         throw new IllegalStateException(
1079                                 "USAP pool state change cause an irrecoverable error",
1080                                 ioe);
1081                     }
1082                 } catch (IOException ioe) {
1083                     // No secondary zygote present.  This is expected on some devices.
1084                 }
1085             }
1086 
1087             // Wait for the response from the primary zygote here so the primary/secondary zygotes
1088             // can work concurrently.
1089             try {
1090                 // Wait for the primary zygote to finish its work.
1091                 primaryZygoteState.mZygoteInputStream.readInt();
1092             } catch (IOException ioe) {
1093                 throw new IllegalStateException(
1094                         "USAP pool state change cause an irrecoverable error",
1095                         ioe);
1096             }
1097         }
1098     }
1099 
1100     /**
1101      * Starts a new zygote process as a child of this zygote. This is used to create
1102      * secondary zygotes that inherit data from the zygote that this object
1103      * communicates with. This returns a new ZygoteProcess representing a connection
1104      * to the newly created zygote. Throws an exception if the zygote cannot be started.
1105      *
1106      * @param processClass The class to use as the child zygote's main entry
1107      *                     point.
1108      * @param niceName A more readable name to use for the process.
1109      * @param uid The user-id under which the child zygote will run.
1110      * @param gid The group-id under which the child zygote will run.
1111      * @param gids Additional group-ids associated with the child zygote process.
1112      * @param runtimeFlags Additional flags.
1113      * @param seInfo null-ok SELinux information for the child zygote process.
1114      * @param abi non-null the ABI of the child zygote
1115      * @param acceptedAbiList ABIs this child zygote will accept connections for; this
1116      *                        may be different from <code>abi</code> in case the children
1117      *                        spawned from this Zygote only communicate using ABI-safe methods.
1118      * @param instructionSet null-ok the instruction set to use.
1119      * @param uidRangeStart The first UID in the range the child zygote may setuid()/setgid() to
1120      * @param uidRangeEnd The last UID in the range the child zygote may setuid()/setgid() to
1121      */
startChildZygote(final String processClass, final String niceName, int uid, int gid, int[] gids, int runtimeFlags, String seInfo, String abi, String acceptedAbiList, String instructionSet, int uidRangeStart, int uidRangeEnd)1122     public ChildZygoteProcess startChildZygote(final String processClass,
1123                                                final String niceName,
1124                                                int uid, int gid, int[] gids,
1125                                                int runtimeFlags,
1126                                                String seInfo,
1127                                                String abi,
1128                                                String acceptedAbiList,
1129                                                String instructionSet,
1130                                                int uidRangeStart,
1131                                                int uidRangeEnd) {
1132         // Create an unguessable address in the global abstract namespace.
1133         final LocalSocketAddress serverAddress = new LocalSocketAddress(
1134                 processClass + "/" + UUID.randomUUID().toString());
1135 
1136         final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName(),
1137                                     Zygote.CHILD_ZYGOTE_ABI_LIST_ARG + acceptedAbiList,
1138                                     Zygote.CHILD_ZYGOTE_UID_RANGE_START + uidRangeStart,
1139                                     Zygote.CHILD_ZYGOTE_UID_RANGE_END + uidRangeEnd};
1140 
1141         Process.ProcessStartResult result;
1142         try {
1143             result = startViaZygote(processClass, niceName, uid, gid,
1144                     gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
1145                     abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
1146                     true /* startChildZygote */, null /* packageName */,
1147                     false /* useUsapPool */, extraArgs);
1148         } catch (ZygoteStartFailedEx ex) {
1149             throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
1150         }
1151 
1152         return new ChildZygoteProcess(serverAddress, result.pid);
1153     }
1154 }
1155