diff options
| author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-09-17 17:54:42 +0000 |
|---|---|---|
| committer | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-09-17 17:54:42 +0000 |
| commit | 085f6686c3169a5a3e4e6cff5d3b7e8ae57b92ff (patch) | |
| tree | 4f20b5e8e58c3c08989e32d02060f54b2084a062 | |
| parent | 0f3757a52f6455e010605d05feaadd5d8d56c0b9 (diff) | |
| parent | a3d3f9df79aaa01e99ea2025c524a016f75f0ac3 (diff) | |
| download | platform_packages_apps_Car_Cluster-simpleperf-release.tar.gz platform_packages_apps_Car_Cluster-simpleperf-release.tar.bz2 platform_packages_apps_Car_Cluster-simpleperf-release.zip | |
Snap for 6844436 from a3d3f9df79aaa01e99ea2025c524a016f75f0ac3 to simpleperf-releasesimpleperf-release
Change-Id: Ic4b0260684840aef185d33f238fbcd4d05e4b677
| -rw-r--r-- | AndroidManifest.xml | 7 | ||||
| -rw-r--r-- | res/layout/include_navigation_state.xml | 3 | ||||
| -rw-r--r-- | src/android/car/cluster/ActivityMonitor.java | 3 | ||||
| -rw-r--r-- | src/android/car/cluster/ClusterDisplayProvider.java | 161 | ||||
| -rw-r--r-- | src/android/car/cluster/ClusterRenderingService.java | 52 | ||||
| -rw-r--r-- | src/android/car/cluster/FakeFreeNavigationActivity.java | 6 | ||||
| -rw-r--r-- | src/android/car/cluster/ImageResolver.java | 4 | ||||
| -rw-r--r-- | src/android/car/cluster/LaneView.java | 2 | ||||
| -rw-r--r-- | src/android/car/cluster/MainClusterActivity.java | 64 | ||||
| -rw-r--r-- | src/android/car/cluster/MusicFragment.java | 7 | ||||
| -rw-r--r-- | src/android/car/cluster/MusicFragmentViewModel.java | 2 | ||||
| -rw-r--r-- | src/android/car/cluster/NavStateController.java | 26 | ||||
| -rw-r--r-- | src/android/car/cluster/NetworkedVirtualDisplay.java | 452 | ||||
| -rw-r--r-- | src/android/car/cluster/PipeThread.java | 81 | ||||
| -rw-r--r-- | src/android/car/cluster/SenderThread.java | 47 | ||||
| -rw-r--r-- | src/android/car/cluster/SocketThread.java | 86 |
16 files changed, 182 insertions, 821 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7dc2bb6..1cac376 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -16,7 +16,9 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="android.car.cluster" - android:sharedUserId="android.uid.system"> + coreApp="true" + android:process="android.car.cluster" + android:sharedUserId="android.uid.cluster"> <uses-sdk android:targetSdkVersion="25" android:minSdkVersion="25"/> @@ -67,6 +69,7 @@ android:exported="false" android:showForAllUsers="true" android:theme="@style/Theme.ClusterTheme"> + <meta-data android:name="distractionOptimized" android:value="true"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.DEFAULT"/> @@ -78,8 +81,8 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleInstance" android:resizeableActivity="true" - android:permission="android.car.permission.CAR_DISPLAY_IN_CLUSTER" android:allowEmbedded="true"> + <meta-data android:name="distractionOptimized" android:value="true"/> </activity> </application> </manifest> diff --git a/res/layout/include_navigation_state.xml b/res/layout/include_navigation_state.xml index 5cd6ff5..d5f227c 100644 --- a/res/layout/include_navigation_state.xml +++ b/res/layout/include_navigation_state.xml @@ -28,7 +28,8 @@ android:id="@+id/section_service_status" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical"> + android:orientation="vertical" + android:visibility="invisible"> <TextView android:id="@+id/service_status" diff --git a/src/android/car/cluster/ActivityMonitor.java b/src/android/car/cluster/ActivityMonitor.java index 28c8147..f61572c 100644 --- a/src/android/car/cluster/ActivityMonitor.java +++ b/src/android/car/cluster/ActivityMonitor.java @@ -166,6 +166,9 @@ public class ActivityMonitor { } List<StackInfo> infos = mActivityManager.getAllStackInfos(); for (StackInfo info : infos) { + if (!info.visible) { + continue; + } Set<ActivityListener> listeners = mListeners.get(info.displayId); if (listeners != null && !listeners.isEmpty()) { for (ActivityListener listener : listeners) { diff --git a/src/android/car/cluster/ClusterDisplayProvider.java b/src/android/car/cluster/ClusterDisplayProvider.java index 78be39b..4050099 100644 --- a/src/android/car/cluster/ClusterDisplayProvider.java +++ b/src/android/car/cluster/ClusterDisplayProvider.java @@ -16,134 +16,105 @@ package android.car.cluster; -import android.annotation.Nullable; +import android.car.Car; +import android.car.CarOccupantZoneManager; +import android.car.CarOccupantZoneManager.OccupantZoneInfo; import android.content.Context; -import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; -import android.os.SystemProperties; -import android.text.TextUtils; import android.util.Log; import android.view.Display; -import android.view.DisplayAddress; + +import com.android.internal.util.Preconditions; + +import java.util.List; /** * This class provides a display for instrument cluster renderer. * <p> * By default it will try to provide physical secondary display if it is connected, if secondary - * display is not connected during creation of this class then it will start networked virtual - * display and listens for incoming connections. - * - * @see {@link NetworkedVirtualDisplay} + * display is not connected during creation of this class then it will wait for the display will + * be added. */ public class ClusterDisplayProvider { private static final String TAG = "Cluster.DisplayProvider"; - - private static final String RO_CLUSTER_DISPLAY_PORT = "ro.car.cluster.displayport"; - private static final String PERSIST_CLUSTER_DISPLAY_PORT = - "persist.car.cluster.displayport"; - private static final int NETWORKED_DISPLAY_WIDTH = 1280; - private static final int NETWORKED_DISPLAY_HEIGHT = 720; - private static final int NETWORKED_DISPLAY_DPI = 320; + private static final boolean DEBUG = false; private final DisplayListener mListener; - private final DisplayManager mDisplayManager; + private final Car mCar; + private CarOccupantZoneManager mOccupantZoneManager; - private NetworkedVirtualDisplay mNetworkedVirtualDisplay; - private int mClusterDisplayId = -1; + private int mClusterDisplayId = Display.INVALID_DISPLAY; ClusterDisplayProvider(Context context, DisplayListener clusterDisplayListener) { mListener = clusterDisplayListener; - mDisplayManager = context.getSystemService(DisplayManager.class); + mCar = Car.createCar(context, null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, + (car, ready) -> { + if (!ready) return; + initClusterDisplayProvider(context, (CarOccupantZoneManager) car.getCarManager( + Car.CAR_OCCUPANT_ZONE_SERVICE)); + }); + } + + void release() { + if (mCar != null && mCar.isConnected()) { + mCar.disconnect(); + } + } + + private void initClusterDisplayProvider( + Context context, CarOccupantZoneManager occupantZoneManager) { + Preconditions.checkArgument( + occupantZoneManager != null,"Can't get CarOccupantZoneManager"); + mOccupantZoneManager = occupantZoneManager; + checkClusterDisplayAdded(); + mOccupantZoneManager.registerOccupantZoneConfigChangeListener( + new ClusterDisplayChangeListener()); + } - Display clusterDisplay = getInstrumentClusterDisplay(mDisplayManager); + private void checkClusterDisplayAdded() { + Display clusterDisplay = getClusterDisplay(); if (clusterDisplay != null) { Log.i(TAG, String.format("Found display: %s (id: %d, owner: %s)", clusterDisplay.getName(), clusterDisplay.getDisplayId(), clusterDisplay.getOwnerPackageName())); mClusterDisplayId = clusterDisplay.getDisplayId(); - clusterDisplayListener.onDisplayAdded(clusterDisplay.getDisplayId()); - trackClusterDisplay(null /* no need to track display by name */); - } else { - Log.i(TAG, "No physical cluster display found, starting network display"); - setupNetworkDisplay(context); + mListener.onDisplayAdded(clusterDisplay.getDisplayId()); } } - private void setupNetworkDisplay(Context context) { - mNetworkedVirtualDisplay = new NetworkedVirtualDisplay(context, - NETWORKED_DISPLAY_WIDTH, NETWORKED_DISPLAY_HEIGHT, NETWORKED_DISPLAY_DPI); - String displayName = mNetworkedVirtualDisplay.start(); - trackClusterDisplay(displayName); - } - - private void trackClusterDisplay(@Nullable String displayName) { - mDisplayManager.registerDisplayListener(new DisplayListener() { - @Override - public void onDisplayAdded(int displayId) { - boolean clusterDisplayAdded = false; - - if (displayName == null && mClusterDisplayId == -1) { - mClusterDisplayId = displayId; - clusterDisplayAdded = true; - } else { - Display display = mDisplayManager.getDisplay(displayId); - if (display != null && TextUtils.equals(display.getName(), displayName)) { - mClusterDisplayId = displayId; - clusterDisplayAdded = true; - } - } - - if (clusterDisplayAdded) { - mListener.onDisplayAdded(displayId); - } - } - - @Override - public void onDisplayRemoved(int displayId) { - if (displayId == mClusterDisplayId) { - mClusterDisplayId = -1; - mListener.onDisplayRemoved(displayId); - } - } - - @Override - public void onDisplayChanged(int displayId) { - if (displayId == mClusterDisplayId) { - mListener.onDisplayChanged(displayId); - } + private Display getClusterDisplay() { + List<OccupantZoneInfo> zones = mOccupantZoneManager.getAllOccupantZones(); + int zones_size = zones.size(); + for (int i = 0; i < zones_size; ++i) { + OccupantZoneInfo zone = zones.get(i); + // Assumes that a Car has only one driver. + if (zone.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { + return mOccupantZoneManager.getDisplayForOccupant( + zone, CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER); } - - }, null); + } + Log.e(TAG, "Can't find the OccupantZoneInfo for driver"); + return null; } - private static Display getInstrumentClusterDisplay(DisplayManager displayManager) { - Display[] displays = displayManager.getDisplays(); - Log.d(TAG, "There are currently " + displays.length + " displays connected."); - - final int displayPortPrimary = 0; // primary port should not be instrument cluster. - int displayPort = SystemProperties.getInt(PERSIST_CLUSTER_DISPLAY_PORT, - displayPortPrimary); - if (displayPort == displayPortPrimary) { - displayPort = SystemProperties.getInt(RO_CLUSTER_DISPLAY_PORT, - displayPortPrimary); - if (displayPort == displayPortPrimary) { - return null; + private final class ClusterDisplayChangeListener implements + CarOccupantZoneManager.OccupantZoneConfigChangeListener { + @Override + public void onOccupantZoneConfigChanged(int changeFlags) { + if (DEBUG) Log.d(TAG, "onOccupantZoneConfigChanged changeFlags=" + changeFlags); + if ((changeFlags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY) == 0) { + return; } - } - // match port for system display ( = null getOwnerPackageName()) - // with separate check for main display as main display should be never picked up. - for (Display display : displays) { - if (display.getDisplayId() != Display.DEFAULT_DISPLAY - && display.getOwnerPackageName() == null - && display.getAddress() != null - && display.getAddress() instanceof DisplayAddress.Physical) { - final byte port = ((DisplayAddress.Physical) display.getAddress()).getPort(); - if (displayPort == port) { - return display; + if (mClusterDisplayId == Display.INVALID_DISPLAY) { + checkClusterDisplayAdded(); + } else { + Display clusterDisplay = getClusterDisplay(); + if (clusterDisplay == null) { + mListener.onDisplayRemoved(mClusterDisplayId); + mClusterDisplayId = Display.INVALID_DISPLAY; } } } - return null; } @Override @@ -152,4 +123,4 @@ public class ClusterDisplayProvider { + " clusterDisplayId = " + mClusterDisplayId + "}"; } -} +}
\ No newline at end of file diff --git a/src/android/car/cluster/ClusterRenderingService.java b/src/android/car/cluster/ClusterRenderingService.java index a030d4a..acaa4b2 100644 --- a/src/android/car/cluster/ClusterRenderingService.java +++ b/src/android/car/cluster/ClusterRenderingService.java @@ -15,7 +15,6 @@ */ package android.car.cluster; -import static android.content.Intent.ACTION_USER_SWITCHED; import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.view.Display.INVALID_DISPLAY; @@ -24,7 +23,7 @@ import static java.lang.Integer.parseInt; import android.app.ActivityManager; import android.app.ActivityOptions; -import android.car.CarNotConnectedException; +import android.car.Car; import android.car.cluster.navigation.NavigationState.NavigationStateProto; import android.car.cluster.renderer.InstrumentClusterRenderingService; import android.car.cluster.renderer.NavigationRenderer; @@ -68,7 +67,6 @@ public class ClusterRenderingService extends InstrumentClusterRenderingService i private static final String TAG = "Cluster.Service"; private static final int NAVIGATION_ACTIVITY_RETRY_INTERVAL_MS = 1000; - static final int NAV_STATE_EVENT_ID = 1; static final String LOCAL_BINDING_ACTION = "local"; static final String NAV_STATE_PROTO_BUNDLE_KEY = "navstate2"; @@ -122,23 +120,17 @@ public class ClusterRenderingService extends InstrumentClusterRenderingService i }; public void setActivityLaunchOptions(int displayId, ClusterActivityState state) { - try { - ActivityOptions options = displayId != INVALID_DISPLAY - ? ActivityOptions.makeBasic().setLaunchDisplayId(displayId) - : null; - setClusterActivityLaunchOptions(CarInstrumentClusterManager.CATEGORY_NAVIGATION, - options); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, String.format("activity options set: %s (displayeId: %d)", - options, options.getLaunchDisplayId())); - } - setClusterActivityState(CarInstrumentClusterManager.CATEGORY_NAVIGATION, - state.toBundle()); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, String.format("activity state set: %s", state)); - } - } catch (CarNotConnectedException ex) { - Log.e(TAG, "Unable to update service", ex); + ActivityOptions options = displayId != INVALID_DISPLAY + ? ActivityOptions.makeBasic().setLaunchDisplayId(displayId) + : null; + setClusterActivityLaunchOptions(options); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, String.format("activity options set: %s (displayeId: %d)", + options, options != null ? options.getLaunchDisplayId() : -1)); + } + setClusterActivityState(state); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, String.format("activity state set: %s", state)); } } @@ -180,6 +172,7 @@ public class ClusterRenderingService extends InstrumentClusterRenderingService i public void onDestroy() { super.onDestroy(); mUserReceiver.unregister(this); + mDisplayProvider.release(); } private void launchMainActivity() { @@ -215,16 +208,18 @@ public class ClusterRenderingService extends InstrumentClusterRenderingService i Log.e(TAG, "Failed to resolve the navigation activity"); return null; } - Rect displaySize = new Rect(0, 0, 320, 240); // Arbitrary size, better than nothing. - DisplayManager dm = (DisplayManager) getSystemService(DisplayManager.class); + Rect displaySize = new Rect(0, 0, 240, 320); // Arbitrary size, better than nothing. + DisplayManager dm = getSystemService(DisplayManager.class); Display display = dm.getDisplay(displayId); if (display != null) { display.getRectSize(displaySize); } + setClusterActivityState(ClusterActivityState.create(/* visible= */ true, + /* unobscuredBounds= */ new Rect(0, 0, 240, 320))); return new Intent(Intent.ACTION_MAIN) .setComponent(component) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(CarInstrumentClusterManager.KEY_EXTRA_ACTIVITY_STATE, + .putExtra(Car.CAR_EXTRA_CLUSTER_ACTIVITY_STATE, ClusterActivityState.create(/* visible= */ true, /* unobscuredBounds= */ displaySize).toBundle()); } @@ -363,14 +358,9 @@ public class ClusterRenderingService extends InstrumentClusterRenderingService i case "setUnobscuredArea": { if (args.length > 5) { - Rect unobscuredArea = new Rect(parseInt(args[2]), parseInt(args[3]), - parseInt(args[4]), parseInt(args[5])); - try { - setClusterActivityState(args[1], - ClusterActivityState.create(true, unobscuredArea).toBundle()); - } catch (CarNotConnectedException e) { - Log.i(TAG, "Failed to set activity state.", e); - } + setClusterActivityState(ClusterActivityState.create(true, + new Rect(parseInt(args[2]), parseInt(args[3]), + parseInt(args[4]), parseInt(args[5])))); } else { Log.i(TAG, "wrong format, expected: category left top right bottom"); } diff --git a/src/android/car/cluster/FakeFreeNavigationActivity.java b/src/android/car/cluster/FakeFreeNavigationActivity.java index a836dc0..7b7a46b 100644 --- a/src/android/car/cluster/FakeFreeNavigationActivity.java +++ b/src/android/car/cluster/FakeFreeNavigationActivity.java @@ -17,6 +17,7 @@ package android.car.cluster; import android.app.Activity; +import android.car.Car; import android.content.Intent; import android.graphics.Rect; import android.os.Bundle; @@ -54,10 +55,9 @@ public class FakeFreeNavigationActivity extends Activity { Log.w(TAG, "Received a null intent"); return; } - Bundle bundle = intent.getBundleExtra(CarInstrumentClusterManager.KEY_EXTRA_ACTIVITY_STATE); + Bundle bundle = intent.getBundleExtra(Car.CAR_EXTRA_CLUSTER_ACTIVITY_STATE); if (bundle == null) { - Log.w(TAG, "Received an intent without " + CarInstrumentClusterManager - .KEY_EXTRA_ACTIVITY_STATE); + Log.w(TAG, "Received an intent without " + Car.CAR_EXTRA_CLUSTER_ACTIVITY_STATE); return; } ClusterActivityState state = ClusterActivityState.fromBundle(bundle); diff --git a/src/android/car/cluster/ImageResolver.java b/src/android/car/cluster/ImageResolver.java index 445b7e6..cb5c19b 100644 --- a/src/android/car/cluster/ImageResolver.java +++ b/src/android/car/cluster/ImageResolver.java @@ -44,12 +44,12 @@ public class ImageResolver { /** * Returns a {@link Bitmap} given a request Uri and dimensions */ - Bitmap getBitmap(Uri uri, int width, int height); + Bitmap getBitmap(@NonNull Uri uri, int width, int height); /** * Returns a {@link Bitmap} given a request Uri, dimensions, and offLanesAlpha value */ - Bitmap getBitmap(Uri uri, int width, int height, float offLanesAlpha) throws IllegalArgumentException; + Bitmap getBitmap(@NonNull Uri uri, int width, int height, float offLanesAlpha); } /** diff --git a/src/android/car/cluster/LaneView.java b/src/android/car/cluster/LaneView.java index 2a5ca58..7d48e28 100644 --- a/src/android/car/cluster/LaneView.java +++ b/src/android/car/cluster/LaneView.java @@ -70,7 +70,7 @@ public class LaneView extends LinearLayout { public void setLanes(ImageReference imageReference, ImageResolver imageResolver) { imageResolver - .getBitmap(imageReference, 0, getHeight()) + .getBitmap(imageReference, 0, getHeight(), 0.5f) .thenAccept(bitmap -> { mHandler.post(() -> { removeAllViews(); diff --git a/src/android/car/cluster/MainClusterActivity.java b/src/android/car/cluster/MainClusterActivity.java index 5e19a42..9c76d37 100644 --- a/src/android/car/cluster/MainClusterActivity.java +++ b/src/android/car/cluster/MainClusterActivity.java @@ -16,6 +16,8 @@ package android.car.cluster; import static android.car.cluster.ClusterRenderingService.LOCAL_BINDING_ACTION; +import static android.content.Intent.ACTION_SCREEN_OFF; +import static android.content.Intent.ACTION_USER_PRESENT; import static android.content.Intent.ACTION_USER_SWITCHED; import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.content.PermissionChecker.PERMISSION_GRANTED; @@ -54,6 +56,7 @@ import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProviders; import androidx.viewpager.widget.ViewPager; @@ -110,6 +113,7 @@ public class MainClusterActivity extends FragmentActivity implements private static final int NAVIGATION_ACTIVITY_RETRY_INTERVAL_MS = 1000; private static final int NAVIGATION_ACTIVITY_RELAUNCH_DELAY_MS = 5000; + private final UserReceiver mUserReceiver = new UserReceiver(); private ActivityMonitor mActivityMonitor = new ActivityMonitor(); private final Handler mHandler = new Handler(); private final Runnable mRetryLaunchNavigationActivity = this::tryLaunchNavigationActivity; @@ -172,6 +176,28 @@ public class MainClusterActivity extends FragmentActivity implements mClusterViewModel.setCurrentNavigationActivity(activity); }; + /** + * On user switch the navigation application must be re-launched on the new user. Otherwise + * the navigation fragment will keep showing the application on the previous user. + * {@link MainClusterActivity} is shared between all users (it is not restarted on user switch) + */ + private class UserReceiver extends BroadcastReceiver { + void register(Context context) { + IntentFilter intentFilter = new IntentFilter(ACTION_USER_UNLOCKED); + context.registerReceiverForAllUsers(this, intentFilter, null, null); + } + void unregister(Context context) { + context.unregisterReceiver(this); + } + @Override + public void onReceive(Context context, Intent intent) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Broadcast received: " + intent); + } + tryLaunchNavigationActivity(); + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -202,7 +228,24 @@ public class MainClusterActivity extends FragmentActivity implements mOrderToFacet.get(NAV_FACET_ID).mButton.requestFocus(); mNavStateController = new NavStateController(findViewById(R.id.navigation_state)); - mClusterViewModel = ViewModelProviders.of(this).get(ClusterViewModel.class); + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_USER_PRESENT); + filter.addAction(ACTION_SCREEN_OFF); + registerReceiver(new BroadcastReceiver(){ + @Override + public void onReceive(final Context context, final Intent intent) { + if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)){ + Log.d(TAG, "ACTION_SCREEN_OFF"); + mNavStateController.hideNavigationStateInfo(); + } + else if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) { + Log.d(TAG, "ACTION_USER_PRESENT"); + mNavStateController.showNavigationStateInfo(); + } + } + }, filter); + + mClusterViewModel = new ViewModelProvider(this).get(ClusterViewModel.class); mClusterViewModel.getNavigationFocus().observe(this, focus -> { // If focus is lost, we launch the default navigation activity again. if (!focus) { @@ -230,9 +273,11 @@ public class MainClusterActivity extends FragmentActivity implements mActivityMonitor.start(); + mUserReceiver.register(this); + InMemoryPhoneBook.init(this); - PhoneFragmentViewModel phoneViewModel = ViewModelProviders.of(this).get( + PhoneFragmentViewModel phoneViewModel = new ViewModelProvider(this).get( PhoneFragmentViewModel.class); phoneViewModel.setPhoneStateCallback(new PhoneFragmentViewModel.PhoneStateCallback() { @@ -263,6 +308,7 @@ public class MainClusterActivity extends FragmentActivity implements protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy"); + mUserReceiver.unregister(this); mActivityMonitor.stop(); if (mService != null) { mService.unregisterClient(this); @@ -394,7 +440,7 @@ public class MainClusterActivity extends FragmentActivity implements Intent intent = new Intent(Intent.ACTION_MAIN) .setComponent(navigationActivity) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(CarInstrumentClusterManager.KEY_EXTRA_ACTIVITY_STATE, + .putExtra(Car.CAR_EXTRA_CLUSTER_ACTIVITY_STATE, activityState.toBundle()); Log.d(TAG, "Launching: " + intent + " on display: " + mNavigationDisplay.mDisplayId); @@ -420,7 +466,7 @@ public class MainClusterActivity extends FragmentActivity implements * <ul> * <li>Dynamically detect what's the default navigation activity the user has selected on the * head unit, and obtain the activity marked with - * {@link CarInstrumentClusterManager#CATEGORY_NAVIGATION} from the same package. + * {@link Car#CAR_CATEGORY_NAVIGATION} from the same package. * <li>Let the user select one from settings. * </ul> */ @@ -442,16 +488,6 @@ public class MainClusterActivity extends FragmentActivity implements if (navigationApp == null) { return null; } - - // Check that it has the right permissions - if (pm.checkPermission(Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER, navigationApp.activityInfo - .packageName) != PERMISSION_GRANTED) { - Log.i(TAG, String.format("Package '%s' doesn't have permission %s", - navigationApp.activityInfo.packageName, - Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER)); - return null; - } - return new ComponentName(navigationApp.activityInfo.packageName, navigationApp.activityInfo.name); } catch (URISyntaxException ex) { diff --git a/src/android/car/cluster/MusicFragment.java b/src/android/car/cluster/MusicFragment.java index 34a7ded..540db4d 100644 --- a/src/android/car/cluster/MusicFragment.java +++ b/src/android/car/cluster/MusicFragment.java @@ -15,6 +15,8 @@ */ package android.car.cluster; +import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK; + import android.os.Bundle; import android.util.Size; import android.view.LayoutInflater; @@ -51,9 +53,10 @@ public class MusicFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { FragmentActivity activity = requireActivity(); - PlaybackViewModel playbackViewModel = PlaybackViewModel.get(activity.getApplication()); + PlaybackViewModel playbackViewModel = + PlaybackViewModel.get(activity.getApplication(), MEDIA_SOURCE_MODE_PLAYBACK); MediaSourceViewModel mMediaSourceViewModel = MediaSourceViewModel.get( - activity.getApplication()); + activity.getApplication(), MEDIA_SOURCE_MODE_PLAYBACK); MusicFragmentViewModel innerViewModel = ViewModelProviders.of(activity).get( MusicFragmentViewModel.class); diff --git a/src/android/car/cluster/MusicFragmentViewModel.java b/src/android/car/cluster/MusicFragmentViewModel.java index 769cac8..9fb0fa4 100644 --- a/src/android/car/cluster/MusicFragmentViewModel.java +++ b/src/android/car/cluster/MusicFragmentViewModel.java @@ -48,7 +48,7 @@ public final class MusicFragmentViewModel extends AndroidViewModel { mMediaSourceViewModel = mediaSourceViewModel; mMediaSource = mMediaSourceViewModel.getPrimaryMediaSource(); mAppName = mapNonNull(mMediaSource, MediaSource::getDisplayName); - mAppIcon = mapNonNull(mMediaSource, MediaSource::getRoundPackageIcon); + mAppIcon = mapNonNull(mMediaSource, MediaSource::getCroppedPackageIcon); } LiveData<CharSequence> getAppName() { diff --git a/src/android/car/cluster/NavStateController.java b/src/android/car/cluster/NavStateController.java index facab51..fe4d724 100644 --- a/src/android/car/cluster/NavStateController.java +++ b/src/android/car/cluster/NavStateController.java @@ -15,6 +15,10 @@ */ package android.car.cluster; +import static android.car.cluster.navigation.NavigationState.NavigationStateProto.ServiceStatus.NORMAL; +import static android.car.cluster.navigation.NavigationState.NavigationStateProto.ServiceStatus.REROUTING; +import static android.car.cluster.navigation.NavigationState.NavigationStateProto.ServiceStatus.SERVICE_STATUS_UNSPECIFIED; + import android.annotation.Nullable; import android.car.cluster.navigation.NavigationState.Destination; import android.car.cluster.navigation.NavigationState.Destination.Traffic; @@ -44,6 +48,7 @@ public class NavStateController { private Handler mHandler = new Handler(); + private View mNavigationState; private LinearLayout mSectionManeuver; private LinearLayout mSectionNavigation; private LinearLayout mSectionServiceStatus; @@ -66,6 +71,7 @@ public class NavStateController { * @param container {@link View} containing the navigation state views */ public NavStateController(View container) { + mNavigationState = container; mSectionManeuver = container.findViewById(R.id.section_maneuver); mSectionNavigation = container.findViewById(R.id.section_navigation); mSectionServiceStatus = container.findViewById(R.id.section_service_status); @@ -82,6 +88,14 @@ public class NavStateController { mContext = container.getContext(); } + public void hideNavigationStateInfo() { + mNavigationState.setVisibility(View.INVISIBLE); + } + + public void showNavigationStateInfo() { + mNavigationState.setVisibility(View.VISIBLE); + } + public void setImageResolver(@Nullable ImageResolver imageResolver) { mImageResolver = imageResolver; } @@ -98,7 +112,13 @@ public class NavStateController { return; } - if (state.getServiceStatus() == NavigationStateProto.ServiceStatus.REROUTING) { + NavigationStateProto.ServiceStatus serviceStatus = state.getServiceStatus(); + if (serviceStatus == SERVICE_STATUS_UNSPECIFIED) { + mSectionManeuver.setVisibility(View.INVISIBLE); + mSectionNavigation.setVisibility(View.INVISIBLE); + mSectionServiceStatus.setVisibility(View.INVISIBLE); + return; + } else if (serviceStatus == REROUTING) { mSectionManeuver.setVisibility(View.INVISIBLE); mSectionNavigation.setVisibility(View.INVISIBLE); mSectionServiceStatus.setVisibility(View.VISIBLE); @@ -115,8 +135,8 @@ public class NavStateController { Traffic traffic = destination != null ? destination.getTraffic() : null; String eta = destination != null ? destination.getFormattedDurationUntilArrival().isEmpty() - ? formatEta(destination.getEstimatedTimeAtArrival()) - : destination.getFormattedDurationUntilArrival() + ? formatEta(destination.getEstimatedTimeAtArrival()) + : destination.getFormattedDurationUntilArrival() : null; mEta.setText(eta); mEta.setTextColor(getTrafficColor(traffic)); diff --git a/src/android/car/cluster/NetworkedVirtualDisplay.java b/src/android/car/cluster/NetworkedVirtualDisplay.java deleted file mode 100644 index 56121d0..0000000 --- a/src/android/car/cluster/NetworkedVirtualDisplay.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.car.cluster; - -import android.annotation.NonNull; -import android.content.Context; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; -import android.hardware.display.VirtualDisplay; -import android.media.MediaCodec; -import android.media.MediaCodec.BufferInfo; -import android.media.MediaCodec.CodecException; -import android.media.MediaCodecInfo; -import android.media.MediaCodecInfo.CodecProfileLevel; -import android.media.MediaFormat; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.util.Log; -import android.view.Display; -import android.view.Surface; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.net.ServerSocket; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.util.UUID; - -/** - * This class encapsulates all work related to managing networked virtual display. - * <p> - * It opens a socket and listens on port {@code PORT} for connections, or the emulator pipe. Once - * connection is established it creates virtual display and media encoder and starts streaming video - * to that socket. If the receiving part is disconnected, it will keep port open and virtual - * display won't be destroyed. - */ -public class NetworkedVirtualDisplay { - private static final String TAG = "Cluster." + NetworkedVirtualDisplay.class.getSimpleName(); - - private final String mUniqueId = UUID.randomUUID().toString(); - - private final DisplayManager mDisplayManager; - private final int mWidth; - private final int mHeight; - private final int mDpi; - - private static final int FPS = 25; - private static final int BITRATE = 6144000; - private static final String MEDIA_FORMAT_MIMETYPE = MediaFormat.MIMETYPE_VIDEO_AVC; - - public static final int MSG_START = 0; - public static final int MSG_STOP = 1; - public static final int MSG_SEND_FRAME = 2; - - private static final String PIPE_NAME = "pipe:qemud:carCluster"; - private static final String PIPE_DEVICE = "/dev/qemu_pipe"; - - // Constants shared with emulator in car-cluster-widget.cpp - public static final int PIPE_START = 1; - public static final int PIPE_STOP = 2; - - private static final int PORT = 5151; - - private SenderThread mActiveThread; - private HandlerThread mBroadcastThread = new HandlerThread("BroadcastThread"); - - private VirtualDisplay mVirtualDisplay; - private MediaCodec mVideoEncoder; - private Handler mHandler; - private byte[] mBuffer = null; - private int mLastFrameLength = 0; - - private final DebugCounter mCounter = new DebugCounter(); - - NetworkedVirtualDisplay(Context context, int width, int height, int dpi) { - mDisplayManager = context.getSystemService(DisplayManager.class); - mWidth = width; - mHeight = height; - mDpi = dpi; - - DisplayListener displayListener = new DisplayListener() { - @Override - public void onDisplayAdded(int i) { - final Display display = mDisplayManager.getDisplay(i); - if (display != null && getDisplayName().equals(display.getName())) { - onVirtualDisplayReady(display); - } - } - - @Override - public void onDisplayRemoved(int i) {} - - @Override - public void onDisplayChanged(int i) {} - }; - - mDisplayManager.registerDisplayListener(displayListener, new Handler()); - } - - /** - * Opens socket and creates virtual display asynchronously once connection established. Clients - * of this class may subscribe to - * {@link android.hardware.display.DisplayManager#registerDisplayListener( - * DisplayListener, Handler)} to be notified when virtual display is created. - * Note, that this method should be called only once. - * - * @return Unique display name associated with the instance of this class. - * - * @see {@link Display#getName()} - * - * @throws IllegalStateException thrown if networked display already started - */ - public String start() { - if (mBroadcastThread.isAlive()) { - throw new IllegalStateException("Already started"); - } - - mBroadcastThread.start(); - mHandler = new BroadcastThreadHandler(mBroadcastThread.getLooper()); - mHandler.sendMessage(Message.obtain(mHandler, MSG_START)); - return getDisplayName(); - } - - public void release() { - mHandler.sendMessage(Message.obtain(mHandler, MSG_STOP)); - mBroadcastThread.quitSafely(); - - if (mVirtualDisplay != null) { - mVirtualDisplay.setSurface(null); - mVirtualDisplay.release(); - mVirtualDisplay = null; - } - } - - private String getDisplayName() { - return "Cluster-" + mUniqueId; - } - - private VirtualDisplay createVirtualDisplay() { - Log.i(TAG, "createVirtualDisplay " + mWidth + "x" + mHeight +"@" + mDpi); - return mDisplayManager.createVirtualDisplay(getDisplayName(), mWidth, mHeight, mDpi, - null, 0 /* flags */, null, null ); - } - - private void onVirtualDisplayReady(Display display) { - Log.i(TAG, "onVirtualDisplayReady, display: " + display); - } - - private void startCasting(Handler handler) { - Log.i(TAG, "Start casting..."); - if (mVideoEncoder != null) { - Log.i(TAG, "Already started casting"); - return; - } - mVideoEncoder = createVideoStream(handler); - - if (mVirtualDisplay == null) { - mVirtualDisplay = createVirtualDisplay(); - } - - mVirtualDisplay.setSurface(mVideoEncoder.createInputSurface()); - mVideoEncoder.start(); - Log.i(TAG, "Video encoder started"); - } - - private MediaCodec createVideoStream(Handler handler) { - MediaCodec encoder; - try { - encoder = MediaCodec.createEncoderByType(MEDIA_FORMAT_MIMETYPE); - } catch (IOException e) { - Log.e(TAG, "Failed to create video encoder for " + MEDIA_FORMAT_MIMETYPE, e); - return null; - } - - encoder.setCallback(new MediaCodec.Callback() { - @Override - public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) { - // Nothing to do - } - - @Override - public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, - @NonNull BufferInfo info) { - mCounter.outputBuffers++; - doOutputBufferAvailable(index, info); - } - - @Override - public void onError(@NonNull MediaCodec codec, @NonNull CodecException e) { - Log.e(TAG, "onError, codec: " + codec, e); - mCounter.bufferErrors++; - stopCasting(); - startCasting(handler); - } - - @Override - public void onOutputFormatChanged(@NonNull MediaCodec codec, - @NonNull MediaFormat format) { - Log.i(TAG, "onOutputFormatChanged, codec: " + codec + ", format: " + format); - - } - }, handler); - - configureVideoEncoder(encoder, mWidth, mHeight); - return encoder; - } - - private void doOutputBufferAvailable(int index, @NonNull BufferInfo info) { - mHandler.removeMessages(MSG_SEND_FRAME); - - ByteBuffer encodedData = mVideoEncoder.getOutputBuffer(index); - if (encodedData == null) { - throw new RuntimeException("couldn't fetch buffer at index " + index); - } - - if (info.size != 0) { - encodedData.position(info.offset); - encodedData.limit(info.offset + info.size); - mLastFrameLength = encodedData.remaining(); - if (mBuffer == null || mBuffer.length < mLastFrameLength) { - Log.i(TAG, "Allocating new buffer: " + mLastFrameLength); - mBuffer = new byte[mLastFrameLength]; - } - encodedData.get(mBuffer, 0, mLastFrameLength); - mVideoEncoder.releaseOutputBuffer(index, false); - - // Send this frame asynchronously (avoiding blocking on the socket). We might miss - // frames if the consumer is not fast enough, but this is acceptable. - sendFrameAsync(0); - } else { - Log.e(TAG, "Skipping empty buffer"); - mVideoEncoder.releaseOutputBuffer(index, false); - } - } - - private void sendFrameAsync(long delayMs) { - Message msg = mHandler.obtainMessage(MSG_SEND_FRAME); - mHandler.sendMessageDelayed(msg, delayMs); - } - - private void sendFrame(byte[] buf, int len) { - if (mActiveThread != null) { - mActiveThread.send(buf, len); - } - } - - private void stopCasting() { - Log.i(TAG, "Stopping casting..."); - - if (mVirtualDisplay != null) { - Surface surface = mVirtualDisplay.getSurface(); - if (surface != null) surface.release(); - } - - if (mVideoEncoder != null) { - // Releasing encoder as stop/start didn't work well (couldn't create or reuse input - // surface). - try { - mVideoEncoder.stop(); - mVideoEncoder.release(); - } catch (IllegalStateException e) { - // do nothing, already released - } - mVideoEncoder = null; - } - Log.i(TAG, "Casting stopped"); - } - - private class BroadcastThreadHandler extends Handler { - private static final int MAX_FAIL_COUNT = 10; - private int mFailConnectCounter; - - BroadcastThreadHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_START: - Log.i(TAG, "Received start message"); - - // Make sure mActiveThread cannot start multiple times - if (mActiveThread != null) { - Log.w(TAG, "Trying to start a running thread. Race condition may exist"); - break; - } - - // Failure to connect to either pipe or network returns null - if (mActiveThread == null) { - mActiveThread = tryPipeConnect(); - } - if (mActiveThread == null) { - mActiveThread = tryNetworkConnect(); - } - if (mActiveThread == null) { - // When failed attempt limit is reached, clean up and quit this thread. - mFailConnectCounter++; - if (mFailConnectCounter >= MAX_FAIL_COUNT) { - Log.e(TAG, "Too many failed connection attempts; aborting"); - release(); - throw new RuntimeException("Abort after failed connection attempts"); - } - mHandler.sendMessage(Message.obtain(mHandler, MSG_START)); - break; - } - - try { - mFailConnectCounter = 0; - mCounter.clientsConnected++; - mActiveThread.start(); - startCasting(this); - } catch (Exception e) { - Log.e(TAG, "Failed to start thread", e); - Log.e(TAG, "DebugCounter: " + mCounter); - } - break; - - case MSG_STOP: - Log.i(TAG, "Received stop message"); - stopCasting(); - mCounter.clientsDisconnected++; - if (mActiveThread != null) { - mActiveThread.close(); - try { - mActiveThread.join(); - } catch (InterruptedException e) { - Log.e(TAG, "Waiting for active thread to close failed", e); - } - mActiveThread = null; - } - break; - - case MSG_SEND_FRAME: - if (mActiveThread == null) { - // Stop the chaining signal if there's no client to send to - break; - } - sendFrame(mBuffer, mLastFrameLength); - // We will keep sending last frame every second as a heartbeat. - sendFrameAsync(1000L); - break; - } - } - - // Returns null if can't establish pipe connection - // Otherwise returns the corresponding client thread - private PipeThread tryPipeConnect() { - try { - RandomAccessFile pipe = new RandomAccessFile(PIPE_DEVICE, "rw"); - byte[] temp = new byte[PIPE_NAME.length() + 1]; - temp[PIPE_NAME.length()] = 0; - System.arraycopy(PIPE_NAME.getBytes(), 0, temp, 0, PIPE_NAME.length()); - pipe.write(temp); - - // At this point, the pipe exists, so we will just wait for a start signal - // This is in case pipe still sends leftover stops from last instantiation - int signal = pipe.read(); - while (signal != PIPE_START) { - Log.i(TAG, "Received non-start signal: " + signal); - signal = pipe.read(); - } - return new PipeThread(mHandler, pipe); - } catch (IOException e) { - Log.e(TAG, "Failed to establish pipe connection", e); - return null; - } - } - - // Returns null if can't establish network connection - // Otherwise returns the corresponding client thread - private SocketThread tryNetworkConnect() { - try { - ServerSocket serverSocket = new ServerSocket(PORT); - Log.i(TAG, "Server socket opened"); - Socket socket = serverSocket.accept(); - socket.setTcpNoDelay(true); - socket.setKeepAlive(true); - socket.setSoLinger(true, 0); - - InputStream inputStream = socket.getInputStream(); - OutputStream outputStream = socket.getOutputStream(); - - return new SocketThread(mHandler, serverSocket, inputStream, outputStream); - } catch (IOException e) { - Log.e(TAG, "Failed to establish network connection", e); - return null; - } - } - } - - private static void configureVideoEncoder(MediaCodec codec, int width, int height) { - MediaFormat format = MediaFormat.createVideoFormat(MEDIA_FORMAT_MIMETYPE, width, height); - - format.setInteger(MediaFormat.KEY_COLOR_FORMAT, - MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); - format.setInteger(MediaFormat.KEY_BIT_RATE, BITRATE); - format.setInteger(MediaFormat.KEY_FRAME_RATE, FPS); - format.setInteger(MediaFormat.KEY_CAPTURE_RATE, FPS); - format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); - format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 1 second between I-frames - format.setInteger(MediaFormat.KEY_LEVEL, CodecProfileLevel.AVCLevel31); - format.setInteger(MediaFormat.KEY_PROFILE, - MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); - - codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); - } - - @Override - public String toString() { - return getClass() + "{" - + ", receiver connected: " + (mActiveThread != null) - + ", encoder: " + mVideoEncoder - + ", virtualDisplay" + mVirtualDisplay - + "}"; - } - - private static class DebugCounter { - long outputBuffers; - long bufferErrors; - long clientsConnected; - long clientsDisconnected; - - @Override - public String toString() { - return getClass().getSimpleName() + "{" - + "outputBuffers=" + outputBuffers - + ", bufferErrors=" + bufferErrors - + ", clientsConnected=" + clientsConnected - + ", clientsDisconnected= " + clientsDisconnected - + "}"; - } - } -} diff --git a/src/android/car/cluster/PipeThread.java b/src/android/car/cluster/PipeThread.java deleted file mode 100644 index a8f6308..0000000 --- a/src/android/car/cluster/PipeThread.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.car.cluster; -import android.os.Handler; -import android.util.Log; - -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Thread that can send data to the emulator using a qemud service. - */ -public class PipeThread extends SenderThread { - private static final String TAG = "Cluster." + PipeThread.class.getSimpleName(); - - private RandomAccessFile mPipe; - - /** - * Creates instance of pipe thread that can write to given pipe file. - * - * @param handler {@link Handler} used to message broadcaster. - * @param pipe {@link RandomAccessFile} file already connected to pipe. - */ - PipeThread(Handler handler, RandomAccessFile pipe) { - super(handler); - mPipe = pipe; - } - - public void run() { - try { - int signal = mPipe.read(); - while (signal != NetworkedVirtualDisplay.PIPE_STOP) { - Log.i(TAG, "Received non-stop signal: " + signal); - signal = mPipe.read(); - } - restart(); - } catch (IOException e) { - Log.e(TAG, "Failed to read from pipe"); - restart(); - } - } - - @Override - public void send(byte[] buf, int len) { - try { - // First sends the size prior to sending the data, since receiving side only sees - // the size of the buffer, which could be significant larger than the actual data. - mPipe.write(ByteBuffer.allocate(4) - .order(ByteOrder.LITTLE_ENDIAN).putInt(len).array()); - mPipe.write(buf); - } catch (IOException e) { - Log.e(TAG, "Write to pipe failed"); - restart(); - } - } - - @Override - public void close() { - try { - mPipe.close(); - } catch (IOException e) { - Log.e(TAG, "Failed to close pipe", e); - } - } -} - diff --git a/src/android/car/cluster/SenderThread.java b/src/android/car/cluster/SenderThread.java deleted file mode 100644 index de461e2..0000000 --- a/src/android/car/cluster/SenderThread.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.car.cluster; -import android.os.Handler; -import android.os.Message; -import android.util.Log; - -/** - * This class serves as a template for sending to specific clients of the broadcaster. - */ -public abstract class SenderThread extends Thread { - private static final String TAG = "Cluster.SenderThread"; - - private Handler mHandler; - - SenderThread(Handler handler) { - mHandler = handler; - } - - abstract void send(byte[] buf, int len); - abstract void close(); - - /** - * Tells the broadcasting thread to stop and close everything in progress, and start over again. - * It will kill the current instance of this thread, and produce a new one. - */ - synchronized void restart() { - if (mHandler.hasMessages(NetworkedVirtualDisplay.MSG_START)) return; - Log.i(TAG, "Sending STOP and START msgs to NetworkedVirtualDisplay"); - - mHandler.sendMessage(Message.obtain(mHandler, NetworkedVirtualDisplay.MSG_STOP)); - mHandler.sendMessage(Message.obtain(mHandler, NetworkedVirtualDisplay.MSG_START)); - } -} diff --git a/src/android/car/cluster/SocketThread.java b/src/android/car/cluster/SocketThread.java deleted file mode 100644 index 7f1c61a..0000000 --- a/src/android/car/cluster/SocketThread.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.car.cluster; -import android.os.Handler; -import android.util.Log; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ServerSocket; - -/** - * The thread that will send data on an opened socket. - */ -public class SocketThread extends SenderThread { - private static final String TAG = "Cluster." + SocketThread.class.getSimpleName(); - private ServerSocket mServerSocket; - private OutputStream mOutputStream; - private InputStream mInputStream; - - /** - * Create instance of thread that can write to given open socket. - * - * @param handler {@link Handler} used to message the broadcaster. - * @param serverSocket {@link ServerSocket} should be already opened. - * @param inputStream {@link InputStream} corresponding to opened socket. - * @param outputStream {@link OutputStream} corresponding to opened socket. - */ - SocketThread(Handler handler, ServerSocket serverSocket, InputStream inputStream, - OutputStream outputStream) { - super(handler); - mServerSocket = serverSocket; - mInputStream = inputStream; - mOutputStream = outputStream; - } - - public void run() { - try { - // This read should block until something disconnects (or something - // similar) which should cause an exception, in which case we should - // try to setup again and reconnect - mInputStream.read(); - } catch (IOException e) { - Log.e(TAG, "Socket thread disconnected."); - } - restart(); - } - - @Override - public void send(byte[] buf, int len) { - try { - mOutputStream.write(buf, 0, len); - } catch (IOException e) { - Log.e(TAG, "Failed to write data to socket, retrying connection"); - restart(); - } - } - - @Override - public void close() { - if (mServerSocket != null) { - try { - mServerSocket.close(); - } catch (IOException e) { - Log.w(TAG, "Failed to close server socket, ignoring"); - } - mServerSocket = null; - } - mInputStream = null; - mOutputStream = null; - } -} - |
