1 /*
2  * Copyright (C) 2013 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;
18 
19 import static android.Manifest.permission.TRANSMIT_IR;
20 
21 import android.annotation.EnforcePermission;
22 import android.annotation.RequiresNoPermission;
23 import android.content.Context;
24 import android.content.pm.PackageManager;
25 import android.hardware.IConsumerIrService;
26 import android.hardware.ir.ConsumerIrFreqRange;
27 import android.hardware.ir.IConsumerIr;
28 import android.os.PowerManager;
29 import android.os.RemoteException;
30 import android.os.ServiceManager;
31 import android.util.Slog;
32 
33 public class ConsumerIrService extends IConsumerIrService.Stub {
34     private static final String TAG = "ConsumerIrService";
35 
36     private static final int MAX_XMIT_TIME = 2000000; /* in microseconds */
37 
getHidlHalService()38     private static native boolean getHidlHalService();
halTransmit(int carrierFrequency, int[] pattern)39     private static native int halTransmit(int carrierFrequency, int[] pattern);
halGetCarrierFrequencies()40     private static native int[] halGetCarrierFrequencies();
41 
42     private final Context mContext;
43     private final PowerManager.WakeLock mWakeLock;
44     private final boolean mHasNativeHal;
45     private final Object mHalLock = new Object();
46     private IConsumerIr mAidlService = null;
47 
ConsumerIrService(Context context)48     ConsumerIrService(Context context) {
49         mContext = context;
50         PowerManager pm = (PowerManager)context.getSystemService(
51                 Context.POWER_SERVICE);
52         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
53         mWakeLock.setReferenceCounted(true);
54 
55         mHasNativeHal = getHalService();
56 
57         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONSUMER_IR)) {
58             if (!mHasNativeHal) {
59                 throw new RuntimeException("FEATURE_CONSUMER_IR present, but no IR HAL loaded!");
60             }
61         } else if (mHasNativeHal) {
62             throw new RuntimeException("IR HAL present, but FEATURE_CONSUMER_IR is not set!");
63         }
64     }
65 
66     @Override
67     @RequiresNoPermission
hasIrEmitter()68     public boolean hasIrEmitter() {
69         return mHasNativeHal;
70     }
71 
getHalService()72     private boolean getHalService() {
73         // Attempt to get the AIDL HAL service first
74         final String fqName = IConsumerIr.DESCRIPTOR + "/default";
75         mAidlService = IConsumerIr.Stub.asInterface(
76                         ServiceManager.waitForDeclaredService(fqName));
77         if (mAidlService != null) {
78             return true;
79         }
80 
81         // Fall back to the HIDL HAL service
82         return getHidlHalService();
83     }
84 
throwIfNoIrEmitter()85     private void throwIfNoIrEmitter() {
86         if (!mHasNativeHal) {
87             throw new UnsupportedOperationException("IR emitter not available");
88         }
89     }
90 
91 
92     @Override
93     @EnforcePermission(TRANSMIT_IR)
transmit(String packageName, int carrierFrequency, int[] pattern)94     public void transmit(String packageName, int carrierFrequency, int[] pattern) {
95         super.transmit_enforcePermission();
96 
97         long totalXmitTime = 0;
98 
99         for (int slice : pattern) {
100             if (slice <= 0) {
101                 throw new IllegalArgumentException("Non-positive IR slice");
102             }
103             totalXmitTime += slice;
104         }
105 
106         if (totalXmitTime > MAX_XMIT_TIME ) {
107             throw new IllegalArgumentException("IR pattern too long");
108         }
109 
110         throwIfNoIrEmitter();
111 
112         // Right now there is no mechanism to ensure fair queing of IR requests
113         synchronized (mHalLock) {
114             if (mAidlService != null) {
115                 try {
116                     mAidlService.transmit(carrierFrequency, pattern);
117                 } catch (RemoteException ignore) {
118                     Slog.e(TAG, "Error transmitting frequency: " + carrierFrequency);
119                 }
120             } else {
121                 int err = halTransmit(carrierFrequency, pattern);
122 
123                 if (err < 0) {
124                     Slog.e(TAG, "Error transmitting: " + err);
125                 }
126             }
127         }
128     }
129 
130     @Override
131     @EnforcePermission(TRANSMIT_IR)
getCarrierFrequencies()132     public int[] getCarrierFrequencies() {
133         super.getCarrierFrequencies_enforcePermission();
134 
135         throwIfNoIrEmitter();
136 
137         synchronized(mHalLock) {
138             if (mAidlService != null) {
139                 try {
140                     ConsumerIrFreqRange[] output = mAidlService.getCarrierFreqs();
141                     if (output.length <= 0) {
142                         Slog.e(TAG, "Error getting carrier frequencies.");
143                     }
144                     int[] result = new int[output.length * 2];
145                     for (int i = 0; i < output.length; i++) {
146                         result[i * 2] = output[i].minHz;
147                         result[i * 2 + 1] = output[i].maxHz;
148                     }
149                     return result;
150                 } catch (RemoteException ignore) {
151                     return null;
152                 }
153             } else {
154                 return halGetCarrierFrequencies();
155             }
156         }
157     }
158 }
159