• 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 package android.car.cluster.renderer;
17 
18 import android.annotation.Nullable;
19 import android.car.navigation.CarNavigationInstrumentCluster;
20 import android.graphics.Bitmap;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Message;
25 
26 import java.lang.ref.WeakReference;
27 import java.util.concurrent.CountDownLatch;
28 
29 /**
30  * A wrapper over {@link NavigationRenderer} that runs all its methods in the context of provided
31  * looper. It is guaranteed that all calls will be invoked in order they were called.
32  */
33 // TODO(deanh): Does this class even need to exist?
34 /* package */ class ThreadSafeNavigationRenderer extends NavigationRenderer {
35 
36     private final Handler mHandler;
37     private final NavigationRenderer mRenderer;
38 
39     private final static int MSG_EVENT = 1;
40 
41     /** Creates thread-safe {@link NavigationRenderer}. Returns null if renderer == null */
42     @Nullable
createFor(Looper looper, NavigationRenderer renderer)43     static NavigationRenderer createFor(Looper looper, NavigationRenderer renderer) {
44         return renderer == null ? null : new ThreadSafeNavigationRenderer(looper, renderer);
45     }
46 
ThreadSafeNavigationRenderer(Looper looper, NavigationRenderer renderer)47     private ThreadSafeNavigationRenderer(Looper looper, NavigationRenderer renderer) {
48         mRenderer = renderer;
49         mHandler = new NavigationRendererHandler(looper, renderer);
50     }
51 
52     @Override
getNavigationProperties()53     public CarNavigationInstrumentCluster getNavigationProperties() {
54         if (mHandler.getLooper() == Looper.myLooper()) {
55             return mRenderer.getNavigationProperties();
56         } else {
57             return runAndWaitResult(mHandler,
58                     new RunnableWithResult<CarNavigationInstrumentCluster>() {
59                         @Override
60                         protected CarNavigationInstrumentCluster createResult() {
61                             return mRenderer.getNavigationProperties();
62                         }
63                     });
64         }
65     }
66 
67     @Override
68     public void onEvent(int eventType, Bundle bundle) {
69         mHandler.sendMessage(mHandler.obtainMessage(MSG_EVENT, eventType, 0, bundle));
70     }
71 
72     private static class NavigationRendererHandler extends RendererHandler<NavigationRenderer> {
73 
74         NavigationRendererHandler(Looper looper, NavigationRenderer renderer) {
75             super(looper, renderer);
76         }
77 
78         @Override
79         public void handleMessage(Message msg, NavigationRenderer renderer) {
80             switch (msg.what) {
81                 case MSG_EVENT:
82                     Bundle bundle = (Bundle) msg.obj;
83                     renderer.onEvent(msg.arg1, bundle);
84                     break;
85                 default:
86                     throw new IllegalArgumentException("Msg: " + msg.what);
87             }
88         }
89     }
90 
91     private static <E> E runAndWaitResult(Handler handler, final RunnableWithResult<E> runnable) {
92         final CountDownLatch latch = new CountDownLatch(1);
93 
94         Runnable wrappedRunnable = new Runnable() {
95             @Override
96             public void run() {
97                 runnable.run();
98                 latch.countDown();
99             }
100         };
101 
102         handler.post(wrappedRunnable);
103 
104         try {
105             latch.await();
106         } catch (InterruptedException e) {
107             throw new RuntimeException(e);
108         }
109         return runnable.getResult();
110     }
111 
112     private static abstract class RunnableWithResult<T> implements Runnable {
113         private volatile T result;
114 
115         protected abstract T createResult();
116 
117         @Override
118         public void run() {
119             result = createResult();
120         }
121 
122         public T getResult() {
123             return result;
124         }
125     }
126 
127     private static abstract class RendererHandler<T> extends Handler {
128 
129         private final WeakReference<T> mRendererRef;
130 
131         RendererHandler(Looper looper, T renderer) {
132             super(looper);
133             mRendererRef = new WeakReference<>(renderer);
134         }
135 
136         @Override
137         public void handleMessage(Message msg) {
138             T renderer = mRendererRef.get();
139             if (renderer != null) {
140                 handleMessage(msg, renderer);
141             }
142         }
143 
144         public abstract void handleMessage(Message msg, T renderer);
145     }
146 }
147