1 /** 2 * Copyright (C) 2017 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 com.android.server.broadcastradio.hal2; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.graphics.Bitmap; 22 import android.graphics.BitmapFactory; 23 import android.hardware.radio.ITuner; 24 import android.hardware.radio.RadioManager; 25 import android.hardware.broadcastradio.V2_0.AmFmRegionConfig; 26 import android.hardware.broadcastradio.V2_0.Announcement; 27 import android.hardware.broadcastradio.V2_0.DabTableEntry; 28 import android.hardware.broadcastradio.V2_0.IAnnouncementListener; 29 import android.hardware.broadcastradio.V2_0.IBroadcastRadio; 30 import android.hardware.broadcastradio.V2_0.ICloseHandle; 31 import android.hardware.broadcastradio.V2_0.ITunerSession; 32 import android.hardware.broadcastradio.V2_0.Result; 33 import android.os.ParcelableException; 34 import android.os.RemoteException; 35 import android.util.MutableInt; 36 import android.util.Slog; 37 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.List; 41 import java.util.Objects; 42 import java.util.stream.Collectors; 43 44 class RadioModule { 45 private static final String TAG = "BcRadio2Srv.module"; 46 47 @NonNull private final IBroadcastRadio mService; 48 @NonNull public final RadioManager.ModuleProperties mProperties; 49 50 private RadioModule(@NonNull IBroadcastRadio service, 51 @NonNull RadioManager.ModuleProperties properties) { 52 mProperties = Objects.requireNonNull(properties); 53 mService = Objects.requireNonNull(service); 54 } 55 56 public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) { 57 try { 58 IBroadcastRadio service = IBroadcastRadio.getService(fqName); 59 if (service == null) return null; 60 61 Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>(); 62 service.getAmFmRegionConfig(false, (result, config) -> { 63 if (result == Result.OK) amfmConfig.value = config; 64 }); 65 66 Mutable<List<DabTableEntry>> dabConfig = new Mutable<>(); 67 service.getDabRegionConfig((result, config) -> { 68 if (result == Result.OK) dabConfig.value = config; 69 }); 70 71 RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName, 72 service.getProperties(), amfmConfig.value, dabConfig.value); 73 74 return new RadioModule(service, prop); 75 } catch (RemoteException ex) { 76 Slog.e(TAG, "failed to load module " + fqName, ex); 77 return null; 78 } 79 } 80 81 public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb) 82 throws RemoteException { 83 TunerCallback cb = new TunerCallback(Objects.requireNonNull(userCb)); 84 Mutable<ITunerSession> hwSession = new Mutable<>(); 85 MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR); 86 87 synchronized (mService) { 88 mService.openSession(cb, (result, session) -> { 89 hwSession.value = session; 90 halResult.value = result; 91 }); 92 } 93 94 Convert.throwOnError("openSession", halResult.value); 95 Objects.requireNonNull(hwSession.value); 96 97 return new TunerSession(this, hwSession.value, cb); 98 } 99 100 public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes, 101 @NonNull android.hardware.radio.IAnnouncementListener listener) throws RemoteException { 102 ArrayList<Byte> enabledList = new ArrayList<>(); 103 for (int type : enabledTypes) { 104 enabledList.add((byte)type); 105 } 106 107 MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR); 108 Mutable<ICloseHandle> hwCloseHandle = new Mutable<>(); 109 IAnnouncementListener hwListener = new IAnnouncementListener.Stub() { 110 public void onListUpdated(ArrayList<Announcement> hwAnnouncements) 111 throws RemoteException { 112 listener.onListUpdated(hwAnnouncements.stream(). 113 map(a -> Convert.announcementFromHal(a)).collect(Collectors.toList())); 114 } 115 }; 116 117 synchronized (mService) { 118 mService.registerAnnouncementListener(enabledList, hwListener, (result, closeHnd) -> { 119 halResult.value = result; 120 hwCloseHandle.value = closeHnd; 121 }); 122 } 123 Convert.throwOnError("addAnnouncementListener", halResult.value); 124 125 return new android.hardware.radio.ICloseHandle.Stub() { 126 public void close() { 127 try { 128 hwCloseHandle.value.close(); 129 } catch (RemoteException ex) { 130 Slog.e(TAG, "Failed closing announcement listener", ex); 131 } 132 } 133 }; 134 } 135 136 Bitmap getImage(int id) { 137 if (id == 0) throw new IllegalArgumentException("Image ID is missing"); 138 139 byte[] rawImage; 140 synchronized (mService) { 141 List<Byte> rawList = Utils.maybeRethrow(() -> mService.getImage(id)); 142 rawImage = new byte[rawList.size()]; 143 for (int i = 0; i < rawList.size(); i++) { 144 rawImage[i] = rawList.get(i); 145 } 146 } 147 148 if (rawImage == null || rawImage.length == 0) return null; 149 150 return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length); 151 } 152 } 153