151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling/*
251e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling * Copyright (C) 2013 The Android Open Source Project
351e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling *
451e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling * Licensed under the Apache License, Version 2.0 (the "License");
551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling * you may not use this file except in compliance with the License.
651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling * You may obtain a copy of the License at
751e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling *
851e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling *      http://www.apache.org/licenses/LICENSE-2.0
951e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling *
1051e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling * Unless required by applicable law or agreed to in writing, software
1151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling * distributed under the License is distributed on an "AS IS" BASIS,
1251e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling * See the License for the specific language governing permissions and
1451e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling * limitations under the License.
1551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling */
1651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
1751e95df8f24e9ea30775686b9e324b9a671213dcErik Gillingpackage com.android.server;
1851e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
1951e95df8f24e9ea30775686b9e324b9a671213dcErik Gillingimport android.content.Context;
2051e95df8f24e9ea30775686b9e324b9a671213dcErik Gillingimport android.content.pm.PackageManager;
2151e95df8f24e9ea30775686b9e324b9a671213dcErik Gillingimport android.hardware.IConsumerIrService;
2251e95df8f24e9ea30775686b9e324b9a671213dcErik Gillingimport android.os.PowerManager;
2351e95df8f24e9ea30775686b9e324b9a671213dcErik Gillingimport android.util.Slog;
2451e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
250c9d61f0989e5a85674fbbc24cf06dd3de1fc326Alex Rayimport java.lang.RuntimeException;
2651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
2751e95df8f24e9ea30775686b9e324b9a671213dcErik Gillingpublic class ConsumerIrService extends IConsumerIrService.Stub {
2851e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    private static final String TAG = "ConsumerIrService";
2951e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
3051e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    private static final int MAX_XMIT_TIME = 2000000; /* in microseconds */
3151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
326a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien    private static native boolean halOpen();
336a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien    private static native int halTransmit(int carrierFrequency, int[] pattern);
346a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien    private static native int[] halGetCarrierFrequencies();
3551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
3651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    private final Context mContext;
3751e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    private final PowerManager.WakeLock mWakeLock;
386a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien    private final boolean mHasNativeHal;
3951e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    private final Object mHalLock = new Object();
4051e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
4151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    ConsumerIrService(Context context) {
4251e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        mContext = context;
4351e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        PowerManager pm = (PowerManager)context.getSystemService(
4451e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling                Context.POWER_SERVICE);
4551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
4651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        mWakeLock.setReferenceCounted(true);
4751e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
486a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien        mHasNativeHal = halOpen();
490c9d61f0989e5a85674fbbc24cf06dd3de1fc326Alex Ray        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONSUMER_IR)) {
506a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien            if (!mHasNativeHal) {
510c9d61f0989e5a85674fbbc24cf06dd3de1fc326Alex Ray                throw new RuntimeException("FEATURE_CONSUMER_IR present, but no IR HAL loaded!");
520c9d61f0989e5a85674fbbc24cf06dd3de1fc326Alex Ray            }
536a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien        } else if (mHasNativeHal) {
540c9d61f0989e5a85674fbbc24cf06dd3de1fc326Alex Ray            throw new RuntimeException("IR HAL present, but FEATURE_CONSUMER_IR is not set!");
5551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        }
5651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    }
5751e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
5851e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    @Override
5951e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    public boolean hasIrEmitter() {
606a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien        return mHasNativeHal;
6151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    }
6251e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
6351e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    private void throwIfNoIrEmitter() {
646a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien        if (!mHasNativeHal) {
6551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling            throw new UnsupportedOperationException("IR emitter not available");
6651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        }
6751e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    }
6851e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
6951e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
7051e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    @Override
7151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    public void transmit(String packageName, int carrierFrequency, int[] pattern) {
7251e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.TRANSMIT_IR)
7351e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling                != PackageManager.PERMISSION_GRANTED) {
7451e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling            throw new SecurityException("Requires TRANSMIT_IR permission");
7551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        }
7651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
7751e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        long totalXmitTime = 0;
7851e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
7951e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        for (int slice : pattern) {
8051e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling            if (slice <= 0) {
8151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling                throw new IllegalArgumentException("Non-positive IR slice");
8251e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling            }
8351e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling            totalXmitTime += slice;
8451e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        }
8551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
8651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        if (totalXmitTime > MAX_XMIT_TIME ) {
8751e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling            throw new IllegalArgumentException("IR pattern too long");
8851e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        }
8951e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
9051e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        throwIfNoIrEmitter();
9151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
9251e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        // Right now there is no mechanism to ensure fair queing of IR requests
9351e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        synchronized (mHalLock) {
946a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien            int err = halTransmit(carrierFrequency, pattern);
9551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
9651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling            if (err < 0) {
9751e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling                Slog.e(TAG, "Error transmitting: " + err);
9851e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling            }
9951e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        }
10051e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    }
10151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
10251e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    @Override
10351e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    public int[] getCarrierFrequencies() {
10451e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.TRANSMIT_IR)
10551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling                != PackageManager.PERMISSION_GRANTED) {
10651e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling            throw new SecurityException("Requires TRANSMIT_IR permission");
10751e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        }
10851e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
10951e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        throwIfNoIrEmitter();
11051e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling
11151e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        synchronized(mHalLock) {
1126a24ab1595aa53c97a7f51cc743aae521c89c87aConnor O'Brien            return halGetCarrierFrequencies();
11351e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling        }
11451e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling    }
11551e95df8f24e9ea30775686b9e324b9a671213dcErik Gilling}
116