|
7 | 7 | import android.content.IntentFilter; |
8 | 8 | import android.os.Build; |
9 | 9 | import android.os.Bundle; |
| 10 | +import android.os.Handler; |
| 11 | +import android.os.HandlerThread; |
10 | 12 | import android.os.IBinder; |
11 | 13 |
|
12 | 14 | import androidx.annotation.NonNull; |
13 | 15 |
|
14 | 16 | import java.io.IOException; |
15 | 17 | import java.util.Objects; |
16 | 18 | import java.util.UUID; |
17 | | -import java.util.concurrent.ExecutionException; |
18 | | -import java.util.concurrent.ExecutorService; |
19 | | -import java.util.concurrent.Executors; |
20 | | -import java.util.concurrent.Future; |
21 | | -import java.util.concurrent.LinkedBlockingQueue; |
| 19 | +import java.util.concurrent.Exchanger; |
22 | 20 | import java.util.concurrent.TimeUnit; |
23 | | -import java.util.concurrent.atomic.AtomicReference; |
| 21 | +import java.util.concurrent.TimeoutException; |
24 | 22 |
|
25 | 23 | abstract class NewProcessReceiver extends BroadcastReceiver { |
26 | | - private static boolean waitFor(Process process, long timeout, TimeUnit unit) throws InterruptedException { |
27 | | - long startTime = System.nanoTime(); |
28 | | - long rem = unit.toNanos(timeout); |
| 24 | + public static IBinder start(Context context, AppProcess appProcess, ComponentName componentName) { |
| 25 | + final String token = UUID.randomUUID().toString(); |
29 | 26 |
|
30 | | - do { |
31 | | - try { |
32 | | - process.exitValue(); |
33 | | - return true; |
34 | | - } catch (IllegalArgumentException ex) { |
35 | | - if (rem > 0) |
36 | | - Thread.sleep( |
37 | | - Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100)); |
38 | | - } |
39 | | - rem = unit.toNanos(timeout) - (System.nanoTime() - startTime); |
40 | | - } while (rem > 0); |
41 | | - return false; |
42 | | - } |
| 27 | + final Exchanger<IBinder> exchanger = new Exchanger<>(); |
43 | 28 |
|
44 | | - public static IBinder start(Context context, AppProcess appProcess, ComponentName componentName) { |
45 | | - String token = UUID.randomUUID().toString(); |
46 | | - IntentFilter filter = new IntentFilter(); |
47 | | - filter.addAction(ACTION_SEND_NEW_PROCESS); |
48 | | - LinkedBlockingQueue<AtomicReference<NewProcessResult>> queue = new LinkedBlockingQueue<>(); |
49 | | - BroadcastReceiver receiver = new NewProcessReceiver() { |
| 29 | + HandlerThread worker = new HandlerThread("IPCWorker"); |
| 30 | + worker.start(); |
| 31 | + |
| 32 | + BroadcastReceiver receiver = new BroadcastReceiver() { |
50 | 33 | @Override |
51 | | - void onReceive(NewProcessResult result) { |
52 | | - if (!result.getToken().equals(token)) return; |
53 | | - queue.offer(new AtomicReference<>(result)); |
| 34 | + public void onReceive(Context context, Intent intent) { |
| 35 | + if (token.equals(intent.getStringExtra(NewProcessReceiver.EXTRA_TOKEN))) { |
| 36 | + IBinder binder = intent.getExtras().getBinder(NewProcessReceiver.EXTRA_NEW_PROCESS); |
| 37 | + try { |
| 38 | + exchanger.exchange(binder); |
| 39 | + } catch (InterruptedException ignored) { |
| 40 | + Thread.currentThread().interrupt(); |
| 41 | + } |
| 42 | + } |
54 | 43 | } |
55 | 44 | }; |
56 | | - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) |
57 | | - context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED); |
58 | | - else context.registerReceiver(receiver, filter); |
59 | | - ExecutorService executorService = Executors.newCachedThreadPool(); |
60 | | - Future<AtomicReference<NewProcessResult>> future = executorService.submit(queue::take); |
| 45 | + |
61 | 46 | try { |
62 | | - Process process = appProcess.start(context.getPackageCodePath(), NewProcess.class, new String[]{ |
63 | | - String.format("--package=%s", context.getPackageName()), |
64 | | - String.format("--token=%s", token), |
65 | | - String.format("--component=%s", componentName.flattenToString()) |
66 | | - }); |
67 | | - executorService.execute(() -> { |
68 | | - try { |
69 | | - waitFor(process, 15, TimeUnit.SECONDS); |
70 | | - } catch (Throwable e) { |
71 | | - e.printStackTrace(); |
72 | | - } |
73 | | - queue.offer(new AtomicReference<>()); |
| 47 | + IntentFilter filter = new IntentFilter(ACTION_SEND_NEW_PROCESS); |
| 48 | + Handler handler = new Handler(worker.getLooper()); |
| 49 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
| 50 | + context.registerReceiver(receiver, filter, null, handler, Context.RECEIVER_NOT_EXPORTED); |
| 51 | + } else { |
| 52 | + context.registerReceiver(receiver, filter, null, handler); |
| 53 | + } |
| 54 | + |
| 55 | + appProcess.start(context.getPackageCodePath(), NewProcess.class, new String[]{ |
| 56 | + "--package=" + context.getPackageName(), |
| 57 | + "--token=" + token, |
| 58 | + "--component=" + componentName.flattenToString() |
74 | 59 | }); |
75 | | - NewProcessResult result = future.get().get(); |
76 | | - IBinder binder = result != null ? result.getBinder() : null; |
77 | | - if (binder == null) process.destroy(); |
78 | | - return binder; |
79 | | - } catch (IOException | ExecutionException | InterruptedException e) { |
80 | | - e.printStackTrace(); |
| 60 | + |
| 61 | + return exchanger.exchange(null, 15, TimeUnit.SECONDS); |
| 62 | + |
| 63 | + } catch (TimeoutException | InterruptedException | IOException e) { |
81 | 64 | return null; |
82 | 65 | } finally { |
83 | | - context.unregisterReceiver(receiver); |
84 | | - executorService.shutdown(); |
| 66 | + try { |
| 67 | + context.unregisterReceiver(receiver); |
| 68 | + } catch (Exception ignored) { |
| 69 | + } |
| 70 | + worker.quitSafely(); |
85 | 71 | } |
86 | 72 | } |
87 | 73 |
|
|
0 commit comments