Categories
他山之石

程序员的气场

文章来源: 中华剑(小贱贱)的QQ空间

昨天和市领导还有软件公司一个老总去省委政法委开会,是省政法委关于综治信访维稳中心信息管理系统的征求意见会。到省委大院停车场下车时,看见前面有三个人也往1号楼方向走,其中两人身子有点瘦小,各背着个背包,走路时还有点弯着腰。于是老总说,估计这几个人也是去开会的,应该是搞软件的。待到我们到达会议室时,发现的确那三人就在会议室里,原来就是负责开发管理信息系统的软件公司的技术人员。后来跟老总交谈说他真猜对了,他说这就是程序员的气场 🙁 。于是让我想起《我是业主》里的李大辉。 😀

Categories
Android CDMA

Android CDMA 手机开发要点

Android从1.6版本开始支持CDMA,cdma-import分支由Teleca CDMA团队开发,最后合并到主干。浏览 http://android.git.kernel.org, 在heads可以查看cdma-import的代码变化,从ril层到上层的应用程序。

RIL

多了下面的请求号:

RIL_REQUEST_CDMA_SET_SUBSCRIPTION
RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE
RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE
RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE
RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE
RIL_REQUEST_CDMA_FLASH
RIL_REQUEST_CDMA_BURST_DTMF
RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY
RIL_REQUEST_CDMA_SEND_SMS
RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE
RIL_REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG
RIL_REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG
RIL_REQUEST_CDMA_SMS_BROADCAST_ACTIVATION
RIL_REQUEST_CDMA_SUBSCRIPTION
RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM
RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM

自动上报:

RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL
RIL_UNSOL_CDMA_CALL_WAITING
RIL_UNSOL_CDMA_OTA_PROVISION_STATUS
RIL_UNSOL_CDMA_INFO_REC
RIL_UNSOL_RESPONSE_CDMA_NEW_SMS

主要工作在 hardware/ril/reference-cdma-sms 实现cdma 短信的编解码,模组特殊AT处理。

Telephony Framework

在 telephony framework层,该启动 CDMAPhone 还是 GSMPhone ?

frameworks/base/telephony/java/com/android/internal/telephony/PhoneFactory.java 里的makeDefaultPhone函数:

//Get preferredNetworkMode from Settings.System
int networkMode = Settings.Secure.getInt(context.getContentResolver(),
        Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode);

如果要设置成EvDo网络,则需要在init.rc配置文件里添加:
setprop ro.telephony.default_network 6
RILConstants.java有对应的值。

如果设置错了,怎么办?在类PhoneProxy中处理消息 EVENT_RADIO_TECHNOLOGY_CHANGE,进行纠正,选择是使用GSM还是CDMA,给PhoneApp发送一个Intent通知radio technology变化。

APP
应用不需要做太大的修改,其中设置应用添加与中国电信CDMA特性相关的,比如接入点名称:
中国电信互联星空设置 ctwap
中国电信互联网设置 ctnet

这些在国外手机是不需要分什么wap、net的,但中国电信CDMA的天翼视讯、天翼阅读仅支持ctwap接入。

Categories
Android

关于 android RIL 调试

首先,要了解RIL是如何启动的,拿G1做个例子

init.rc:

service ril-daemon /system/bin/rild
    socket rild stream 660 root radio
    socket rild-debug stream 660 radio system
    user root
    group radio cache inet misc audio

/system/build.prop:

ro.ril.hsxpa=1
ro.ril.gprsclass=10
rild.libpath=/system/lib/libhtc_ril.so

阅读 /hardware/ril/rild/rild.c。

如果/system/bin/rild 启动有带参数,则解析参数,否则找系统变量 rild.libpath

也可以不按照G1的,一口气写完:
init.rc:
/system/bin/rild -l /system/lib/libreference-ril.so — -d /dev/ttyS0

/hardware/ril/rild/rild.c 里有一段注释: “special override when in the emulator”, 紧接着是 #if 1 … #endif 代码块。
它读取 /proc/cmdline,

# cat /proc/cmdline ,模拟器上的值
qemu=1 console=ttyS0 android.checkjni=1 android.qemud=ttyS1 android.ndns=2

找到与字符串“android.qemud“匹配的,则表示运行在模拟器上,会覆盖ril系统设置,连接为模拟器准备的 /dev/socket/qemud

所以,在开发生产版本,要把 #if 1 修改为 #if 0, 或者在编译kernel里把生成的 /proc/cmdline 配置去掉android.qemud。

要调试 RIL,最好的方法就是打开 radio的log:

$ adb logcat -b radio

最好加上 log语法亮度工具 coloredlogcat.py,一些常见的LOG TAG要明白,他们是:

RIL: /hardware/ril/reference-ril/refereince-ril.c
AT: /hardware/ril/reference-ril/atchannel.c
RILD: /hardware/ril/rild/rild.c
RILC: /hardware/ril/libril/ril.cpp
RILB frameworks/base/telephony/java/com/android/internal/telephony/BaseCommands.java
RILJ: /frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
GSM: /frameworks/base/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java

如何在模拟器上外接 gsm modem?

请参考 http://i-miss-erin.blogspot.com/2009/09/android-emulator-external-gsm-modem.html

Categories
Android

PhoneFactory.getDefaultPhone must be called from Looper thread

写了一个设置、显示本机号码的小工具(A small tool displays and settings my phone number),详见”Android设置显示本机号码“。照着Settings写的,运行时,遇到了:
1487 AndroidRuntime E Caused by: java.lang.RuntimeException: PhoneFactory.getDefaultPhone must be called from Looper thread
1487 AndroidRuntime E at com.android.internal.telephony.PhoneFactory.getDefaultPhone(PhoneFactory.java:155)
1487 AndroidRuntime E ... 24 more

目前好像除了PhoneApp,其他应用程序是无法调用
com.android.internal.telephony.Phone;
com.android.internal.telephony.PhoneFactory
的,一般是通过发intent消息。仔细对比了一下,发现Settings的AndroidManifest.xml用到Phone的Activity属性添加了:

android:process="com.android.phone"

在AndroidManifest.xml 把android:sharedUserId=”android.uid.system”也加上,问题解决。像CM的固件, 编译的时候,android.uid.system 是 Android 默认的公共签名。如果使用android.uid.system的apk签名不不一样,是不能装的,会报错:ERROR/PackageManager(81): Package org.lytsing.myphonenumber has no signatures that match those in shared user android.uid.system; ignoring!

当然,android手机第三方开发者不能用这个方法。网上还有其他一些解决办法,参看:
http://stackoverflow.com/questions/2143754/can-a-telephony-phone-object-be-instantiated-through-the-sdk 原理是通过AIDL及反射机制,使用隐藏API。

Categories
Android

Android 组件: SectionedAdapter

这个组件与Mark Murphy 的书《The Busy Coder’s Guide to Advanced Android Development》有一些不同,这个SectionedAdapter是通过干坏事,反编译Vending.apk得到的。还有一个是AggregatedAdapter,合起来实现market单个软件信息显示的效果。
android market asset info

不过AggregatedAdapter比较难反编译出来,没关系,加上cwac-merge这个组件,就可以实现同样的效果。不同分段的Adapter,继承SectionedAdapter,实现自己的东西,该干啥就干啥,然后merge起来。

SectionAdapter.java

/*
 * Copyright (C) 2010 lytsing.org
 *
 * 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 org.lytsing.adapters;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

/**
 * abstract SectionAdapter, difference from
 * cw-advandroid/ListView/Sections/src/com/commonsware/android/listview/SectionedAdapter.java
 * just decompile from Vending.apk :-)
 *
 */
abstract public class SectionAdapter extends BaseAdapter {
    protected int mCount;
    protected boolean mDeactivated;
    protected View mSectionHeaderView;

    /**
     * Constructor
     * 
     * @param sectionTitleId The resource ID for a layout file containing a layout to use when
     *                  instantiating views.
     * @param context The current context.
     * @param parent The parent that this view will eventually be attached to
     */
    public SectionAdapter(int sectionTitleId, Context context, ViewGroup parent) {
        this(Util.inflateView(R.layout.section_header, context, parent));
        ((TextView)mSectionHeaderView).setText(sectionTitleId);
    }

    /**
     * Constructor
     * 
     * @param sectionHeaderView The header view
     */
    public SectionAdapter(View sectionHeaderView) {
        mSectionHeaderView = sectionHeaderView;
        mDeactivated = false;
        mCount = 0;
    }

    public void activate() {
        if (mDeactivated) {
            mDeactivated = false;
            notifyDataSetChanged();
        }
    }

    /**
     * Are all items in this SectionAdapter enable?
     * If yes it means all items are selectable and clickable.
     */
    public boolean areAllItemsEnabled() {
        return false;
    }

    public void deactivate() {
        if (mDeactivated == false) {
            mDeactivated = true;
            notifyDataSetChanged();
        }
    }

    /**
     * How many items are in the data set represented by this
     * Adapter.
     */
    public int getCount() {
        if (mDeactivated) {
            return 0;
        } else {
            return mCount + 1; // add one for header
        }
    }

    /**
     * Get the data item associated with the specified
     * position in the data set.
     * 
     * @param position Position of the item whose data we want
     */
    public Object getItem(int position) {
        if (position == 0) {
            return mSectionHeaderView;
        }
        
        return null;
    }

    /**
     * Get the row id associated with the specified position
     * in the list.
     * 
     * @param position Position of the item whose data we want
     */
    public long getItemId(int position) {
        if (position == 0) {
            return mSectionHeaderView.getId();
        } 
        
        return 0;
    }

    /**
     * Get a View that displays the data at the specified
     * position in the data set.
     * 
     * @param position Position of the item whose data we want
     * @param convertView View to recycle, if not null
     * @param parent ViewGroup containing the returned View
     */
    public View getView(int position, View convertView, ViewGroup parent) {

        return position == 0 ? mSectionHeaderView : null;
    }

    /**
     * Returns true if the item at the specified position is not a separator
     * (A separator is a non-selectable, non-clickable item).
     * 
     * @param position Index of the item
     * @return True if the item is not a separator
     */
    public boolean isEnabled(int position) {
        return false;
    }
}

Util.java

/*
 * Copyright (C) 2010 lytsing.org
 *
 * 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 org.lytsing.adapters;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * Util 
 *
 */
public class Util {

    private Util() {
    }
    
    /**
     * Inflate a new view hierarchy from the specified XML resource.
     * 
     * @param resource ID for an XML layout resource to load 
     * @param context The current context.
     * @return The root View of the inflated XML file.
     */
    public static View inflateView(int resource, Context context) {

        return inflateView(resource, context, null);
    }

    /**
     * Inflate a new view hierarchy from the specified xml resource.
     * 
     * @param resourceID for an XML layout resource to load (e.g.,
     * @param context The current context.
     * @param parent simply an object that provides a set of LayoutParams
     *        values for root of the returned hierarchy
     * @return The root View of the inflated XML file.
     */
    public static View inflateView(int resource, Context context, ViewGroup parent) {
        LayoutInflater vi = (LayoutInflater)context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        return  vi.inflate(resource, parent, false);
    }
}

res/layout/section_header.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:textAppearance="?android:attr/textAppearanceSmall"
    android:textStyle="bold"
    android:gravity="center_vertical"
    android:background="@android:drawable/dark_header"
    android:paddingLeft="8.0dip"
    android:layout_width="fill_parent"
    android:layout_height="26.0dip"
    >
</TextView>

代码下载; https://github.com/lytsing/SectionedAdapter
参考阅读:
Jeff Sharkey’s blog : Separating Lists with Headers in Android 0.9
androidguys: CWAC’d Up: Alternative Adapters
本博客文章: 千万不要把Listview放在ScrollView里

Pages: 1 2 Next