2016/5/26

攔截VOLUME UP 鍵

ref: https://gitlab.com/checko/InterceptVolumeUp

就是用 onKeyDown, onKeyUp

public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode==KeyEvent.KEYCODE_VOLUME_UP) {
            Log.i(TAG,event.toString());
            return true;
        }

        return super.onKeyDown(keyCode,event);
    }
記得,處理過(攔截), 就要 return true.
不處理,交給系統的話,就要 call super.onKeyDown/Up
不能只return false,
否則還是沒有動做。

2016/5/25

svn : set executable attrib to files

svn 如果一開始 add file 時,沒有加 executable attrib, 之後要加,他都當沒看到。
所以要用 svn 特別為 file attrib 設的command。

像,要把所有 .sh file 都加上 x:
$ svn propset svn:executable "777" *.sh

設完後,用 svn status 就可以看到 file 被改變了。
就可以commit


ref:http://stackoverflow.com/questions/6874085/how-to-set-execute-attribute-to-a-file-and-check-it-in-svn-from-windows

2016/5/23

2016/5/19

Preload resource warnning..

出現一堆 message:
W/Resources(  220): Preloaded drawable resource #0x10802a5 (android:drawable/dialog_top_holo_dark) that varies with configuration!!
W/Resources(  220): Preloaded drawable resource #0x10802a6 (android:drawable/dialog_top_holo_light) that varies with configuration!!
W/Resources(  220): Preloaded drawable resource #0x1080358 (android:drawable/ic_dialog_alert_holo_dark) that varies with configuration!!
W/Resources(  220): Preloaded drawable resource #0x1080359 (android:drawable/ic_dialog_alert_holo_light) that varies with configuration!!
W/Resources(  220): Preloaded drawable resource #0x1080496 (android:drawable/list_divider_holo_dark) that varies with configuration!!
W/Resources(  220): Preloaded drawable resource #0x1080497 (android:drawable/list_divider_holo_light) that varies with configuration!!
W/Resources(  220): Preloaded drawable resource #0x1080497 (android:drawable/list_divider_holo_light) that varies with configuration!!
W/Resources(  220): Preloaded drawable resource #0x10800c2 (android:drawable/ab_transparent_dark_holo) that varies with configuration!!

grep 一下,出自於:frameworks/base/core/java/android/content/res/Resources.java 2240,12 84%
    private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
            int resourceId, String name) {
        // We allow preloading of resources even if they vary by font scale (which
        // doesn't impact resource selection) or density (which we handle specially by
        // simply turning off all preloading), as well as any other configs specified
        // by the caller.
        if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
                ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) {
            String resName;
            try {
                resName = getResourceName(resourceId);
            } catch (NotFoundException e) {
                resName = "?";
            }
            // This should never happen in production, so we should log a
            // warning even if we're not debugging.
            Log.w(TAG, "Preloaded " + name + " resource #0x"
                    + Integer.toHexString(resourceId)
                    + " (" + resName + ") that varies with configuration!!");
            return false;
        }
        if (TRACE_FOR_PRELOAD) {
            String resName;
            try {
                resName = getResourceName(resourceId);
            } catch (NotFoundException e) {
                resName = "?";
            }
            Log.w(TAG, "Preloading " + name + " resource #0x"
                    + Integer.toHexString(resourceId)
                    + " (" + resName + ")");
        }
        return true;
    }

MasterVolume..

framework/base/media/java/android/media/AudioService.java
    public void setMasterVolume(int volume, int flags, String callingPackage) {
        if (mUseFixedVolume) {
            return;
        }

        if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(),
                callingPackage) != AppOpsManager.MODE_ALLOWED) {
            return;
        }

        if (volume < 0) {
            volume = 0;
        } else if (volume > MAX_MASTER_VOLUME) {
            volume = MAX_MASTER_VOLUME;
        }
        doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
    }

    private void doSetMasterVolume(float volume, int flags) {
        // don't allow changing master volume when muted
        if (!AudioSystem.getMasterMute()) {
            int oldVolume = getMasterVolume();
            AudioSystem.setMasterVolume(volume);

            int newVolume = getMasterVolume();
            if (newVolume != oldVolume) {
                // Post a persist master volume msg
                sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
                        Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
            }
            // Send the volume update regardless whether there was a change.
            sendMasterVolumeUpdate(flags, oldVolume, newVolume);
        }
    }

最後call 到 framework/av/service/audioflinger/AudioFlinger.cpp
status_t AudioFlinger::setMasterVolume(float value)
{
 ....
 ...
    // Set master volume in the HALs which support it.
    for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
        AutoMutex lock(mHardwareLock);
        AudioHwDevice *dev = mAudioHwDevs.valueAt(i);

        mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
        if (dev->canSetMasterVolume()) {
            dev->hwDevice()->set_master_volume(dev->hwDevice(), value);
        }
        mHardwareStatus = AUDIO_HW_IDLE;
    }

所以 AudioHwDevice 要 implement canSetMasterVolume( ), 然後實做 set_master_volume( )

再查 canSetMasterVolume..
一樣,在 AudioFlinger.h:
    class AudioHwDevice {
         ...
         ...
        bool canSetMasterVolume() const {
            return (0 != (mFlags & AHWD_CAN_SET_MASTER_VOLUME));
        }


mFlag 是在 AudioHwDevice 生成時給定的...
        AudioHwDevice(audio_module_handle_t handle,
                      const char *moduleName,
                      audio_hw_device_t *hwDevice,
                      Flags flags)
            : mHandle(handle), mModuleName(strdup(moduleName))
            , mHwDevice(hwDevice)
            , mFlags(flags) { }

同樣的地方...
audioflinger 在 load audio.xxx.xxx module 時,會測試 audio module 是否有 implement set_master_volume( )
好決定生成 AudioHwDevice 時,flag 要不要加上 AHWD_CAN_SET_MASTER_VOLUME:
    AudioHwDevice::Flags flags = static_cast<AudioHwDevice::Flags>(0);
    {  
        ....
        ....
        mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
        if ((NULL != dev->set_master_volume) &&
            (OK == dev->set_master_volume(dev, mMasterVolume))) {
            flags = static_cast<AudioHwDevice::Flags>(flags |
                    AudioHwDevice::AHWD_CAN_SET_MASTER_VOLUME);
        }

        mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
        if ((NULL != dev->set_master_mute) &&
            (OK == dev->set_master_mute(dev, mMasterMute))) {
            flags = static_cast<AudioHwDevice::Flags>(flags |
                    AudioHwDevice::AHWD_CAN_SET_MASTER_MUTE);
        }
        ...
     }
     ...
    audio_module_handle_t handle = nextUniqueId();
    mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));


然後在 hw_module 的 source (imx6): hardware/imx/alsa/tinyalsa_hal.c:
static int adev_open(const hw_module_t* module, const char* name,
                     hw_device_t** device)
{
    ....
    adev->hw_device.set_master_volume       = adev_set_master_volume;

有 module function table 的 assign,
implement 是:
static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
{
    return -ENOSYS;
}
.. 也就是說...沒有提供這個功能。

所以把 return value 從 -ENOSYS 改成 0

然後,還要改...
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8006659..ecb8780 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -65,7 +65,7 @@
 
     <!-- Flag indicating that the media framework should allow changing
          master volume stream and nothing else . -->
-    <bool name="config_useMasterVolume">false</bool>
+    <bool name="config_useMasterVolume">true</bool>
叫 ..
./media/java/android/media/AudioManager.java:585:                com.android.internal.R.bool.config_useMasterVolume);
./media/java/android/media/AudioService.java:641:                com.android.internal.R.bool.config_useMasterVolume);
這兩個 source 用 MasterVolume

另外 UI:

2016/5/18

handle Key Event 的位置..

framework/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java 有一段:
    protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) {
        /* ****************************************************************************
         * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES.
         *
         * If your key handling must happen before the app gets a crack at the event,
         * it goes in PhoneWindowManager.
         *
         * If your key handling should happen in all windows, and does not depend on
         * the state of the current application, other than that the current
         * application can override the behavior by handling the event itself, it
         * should go in PhoneFallbackEventHandler.
         *
         * Only if your handling depends on the window, and the fact that it has
         * a DecorView, should it go here.
         * ****************************************************************************/
  • 在所有 app 能拿到之前 : PhoneWindowManager
  • 除非 app 有自己處理,否則交給你處理: PhoneFallbackEventHandler
  • 你有用到 DecoView... : PhoneWindow.java

android safe mode..

在 framework/base/service/com/android/server/wm/WindowManagerService.java:
    public boolean detectSafeMode() {
        if (!mInputMonitor.waitForInputDevicesReady(
                INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
            Slog.w(TAG, "Devices still not ready after waiting "
                   + INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS
                   + " milliseconds before attempting to detect safe mode.");
        }

        int menuState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY,
                KeyEvent.KEYCODE_MENU);
        int sState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_S);
        int dpadState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD,
                KeyEvent.KEYCODE_DPAD_CENTER);
        int trackballState = mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL,
                InputManagerService.BTN_MOUSE);
        int volumeDownState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY,
                KeyEvent.KEYCODE_VOLUME_DOWN);
        mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0
                || volumeDownState > 0;
        try {
            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0) {
                mSafeMode = true;
                SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, "");
            }
        } catch (IllegalArgumentException e) {
        }
        if (mSafeMode) {
            Log.i(TAG, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
                    + " dpad=" + dpadState + " trackball=" + trackballState + ")");
        } else {
            Log.i(TAG, "SAFE MODE not enabled");
        }
        mPolicy.setSafeMode(mSafeMode);
        return mSafeMode;
    }

所以有很多方式(按鍵)進入 safemode:
  • menu button
  • dpad (?) button
  • mouse button
  • volume down

ref:https://www.asus.com/tw/support/faq/1005163

2016/5/11

ALSA driver, add support 24000Hz Sample Rate..

include/uapi/sound/asound.h
#define SNDRV_MASK_MAX  256

struct snd_mask {
        __u32 bits[(SNDRV_MASK_MAX+31)/32];
};


tinypcminfo : 會列出 sound card 的 property (rules), 例如:
root@sabreauto_6q:/ # tinypcminfo -D 0                                         
Info for card 0, device 0:

PCM out:
      Access: 0x000009
   Format[0]: 0x000045
   Format[1]: 00000000
 Format Name: S8, S16_LE, S24_LE
   Subformat: 0x000001
        Rate: min=48000Hz max=192000Hz
    Channels: min=1  max=12
 Sample bits: min=8  max=32
 Period size: min=3  max=65535
Period count: min=2  max=255

PCM in:
      Access: 0x000009
   Format[0]: 0x000045
   Format[1]: 00000000
 Format Name: S8, S16_LE, S24_LE
   Subformat: 0x000001
        Rate: min=48000Hz max=192000Hz
    Channels: min=1  max=8
 Sample bits: min=8  max=32
 Period size: min=4  max=65535
Period count: min=2  max=255


然後看到 fsl_esai.c 李有..
#define FSL_ESAI_RATES          SNDRV_PCM_RATE_8000_192000
...
...
        .probe = fsl_esai_dai_probe,
        .playback = {
                .stream_name = "esai-Playback",
                .channels_min = 1,
                .channels_max = 12,
                .rates = FSL_ESAI_RATES,
                .formats = FSL_ESAI_FORMATS,
        },
        .capture = {
                .stream_name = "esai-Capture",
                .channels_min = 1,
                .channels_max = 8,
                .rates = FSL_ESAI_RATES,
                .formats = FSL_ESAI_FORMATS,
        },
        .ops = &fsl_esai_dai_ops,
};


找一下這個..
include/sound/pcm.h:

/* If you change this don't forget to change rates[] table in pcm_native.c */
#define SNDRV_PCM_RATE_5512             (1<<0)          /* 5512Hz */
#define SNDRV_PCM_RATE_8000             (1<<1)          /* 8000Hz */
#define SNDRV_PCM_RATE_11025            (1<<2)          /* 11025Hz */
#define SNDRV_PCM_RATE_16000            (1<<3)          /* 16000Hz */
#define SNDRV_PCM_RATE_22050            (1<<4)          /* 22050Hz */
#define SNDRV_PCM_RATE_32000            (1<<5)          /* 32000Hz */
#define SNDRV_PCM_RATE_44100            (1<<6)          /* 44100Hz */
#define SNDRV_PCM_RATE_48000            (1<<7)          /* 48000Hz */
#define SNDRV_PCM_RATE_64000            (1<<8)          /* 64000Hz */
#define SNDRV_PCM_RATE_88200            (1<<9)          /* 88200Hz */
#define SNDRV_PCM_RATE_96000            (1<<10)         /* 96000Hz */
#define SNDRV_PCM_RATE_176400           (1<<11)         /* 176400Hz */
#define SNDRV_PCM_RATE_192000           (1<<12)         /* 192000Hz */

#define SNDRV_PCM_RATE_CONTINUOUS       (1<<30)         /* continuous range */
#define SNDRV_PCM_RATE_KNOT             (1<<31)         /* supports more non-continuos rates */

#define SNDRV_PCM_RATE_8000_44100       (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_11025|\
                                         SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_22050|\
                                         SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100)
#define SNDRV_PCM_RATE_8000_48000       (SNDRV_PCM_RATE_8000_44100|SNDRV_PCM_RATE_48000)
#define SNDRV_PCM_RATE_8000_96000       (SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_64000|\
                                         SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
#define SNDRV_PCM_RATE_8000_192000      (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\

裡面沒有 24000....

又,有 comment..." If you change this don't forget to change rates[] table in pcm_native.c "
大概是可以自己修改的意思,所以,...插入一個 24000

然後去看看 pcm_native.c..
也有對應的 code, 和檢查,所以照著修改...
#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 13
#error "Change this table"
#endif

static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 24000, 32000, 44100,
                                 48000, 64000, 88200, 96000, 176400, 192000 };
這樣 build 完就可以 support 2400 了...


alsa driver 決定 sound card 的 sample rate range 是由一堆rules 取交集,
這些 rule 可能是 i2s, sound card, hardwae, software driver..
最後 pcm_native.c 會把所有的 rule 做交集取出有效的 range.

2016/5/6

  • 應該要把 jni 獨立在令一個 class, 不要和 MainActivity 關聯,這樣以後 refactory 比較方便
  • 要設法處理每個 view 中 widget/control 的 value 保存問題。尤其是共用 layout 和 handler 時,要分離出保存value 的code 有點不方便